diff --git a/Crypto/src/crypto/AsymmetricCipherKeyPair.cs b/Crypto/src/crypto/AsymmetricCipherKeyPair.cs
new file mode 100644
index 000000000..b00a3dc02
--- /dev/null
+++ b/Crypto/src/crypto/AsymmetricCipherKeyPair.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * a holding class for public/private parameter pairs.
+ */
+ public class AsymmetricCipherKeyPair
+ {
+ private readonly AsymmetricKeyParameter publicParameter;
+ private readonly AsymmetricKeyParameter privateParameter;
+
+ /**
+ * basic constructor.
+ *
+ * @param publicParam a public key parameters object.
+ * @param privateParam the corresponding private key parameters.
+ */
+ public AsymmetricCipherKeyPair(
+ AsymmetricKeyParameter publicParameter,
+ AsymmetricKeyParameter privateParameter)
+ {
+ if (publicParameter.IsPrivate)
+ throw new ArgumentException("Expected a public key", "publicParameter");
+ if (!privateParameter.IsPrivate)
+ throw new ArgumentException("Expected a private key", "privateParameter");
+
+ this.publicParameter = publicParameter;
+ this.privateParameter = privateParameter;
+ }
+
+ /**
+ * return the public key parameters.
+ *
+ * @return the public key parameters.
+ */
+ public AsymmetricKeyParameter Public
+ {
+ get { return publicParameter; }
+ }
+
+ /**
+ * return the private key parameters.
+ *
+ * @return the private key parameters.
+ */
+ public AsymmetricKeyParameter Private
+ {
+ get { return privateParameter; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/AsymmetricKeyParameter.cs b/Crypto/src/crypto/AsymmetricKeyParameter.cs
new file mode 100644
index 000000000..7502ee305
--- /dev/null
+++ b/Crypto/src/crypto/AsymmetricKeyParameter.cs
@@ -0,0 +1,47 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto
+{
+ public abstract class AsymmetricKeyParameter
+ : ICipherParameters
+ {
+ private readonly bool privateKey;
+
+ protected AsymmetricKeyParameter(
+ bool privateKey)
+ {
+ this.privateKey = privateKey;
+ }
+
+ public bool IsPrivate
+ {
+ get { return privateKey; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ AsymmetricKeyParameter other = obj as AsymmetricKeyParameter;
+
+ if (other == null)
+ {
+ return false;
+ }
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ AsymmetricKeyParameter other)
+ {
+ return privateKey == other.privateKey;
+ }
+
+ public override int GetHashCode()
+ {
+ return privateKey.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/BufferedAeadBlockCipher.cs b/Crypto/src/crypto/BufferedAeadBlockCipher.cs
new file mode 100644
index 000000000..87413b69d
--- /dev/null
+++ b/Crypto/src/crypto/BufferedAeadBlockCipher.cs
@@ -0,0 +1,259 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * The AEAD block ciphers already handle buffering internally, so this class
+ * just takes care of implementing IBufferedCipher methods.
+ */
+ public class BufferedAeadBlockCipher
+ : BufferedCipherBase
+ {
+ private readonly IAeadBlockCipher cipher;
+
+ public BufferedAeadBlockCipher(
+ IAeadBlockCipher cipher)
+ {
+ if (cipher == null)
+ throw new ArgumentNullException("cipher");
+
+ this.cipher = cipher;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return cipher.AlgorithmName; }
+ }
+
+ /**
+ * initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public override void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom) parameters).Parameters;
+ }
+
+ cipher.Init(forEncryption, parameters);
+ }
+
+ /**
+ * return the blocksize for the underlying cipher.
+ *
+ * @return the blocksize for the underlying cipher.
+ */
+ public override int GetBlockSize()
+ {
+ return cipher.GetBlockSize();
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public override int GetUpdateOutputSize(
+ int length)
+ {
+ return cipher.GetUpdateOutputSize(length);
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public override int GetOutputSize(
+ int length)
+ {
+ return cipher.GetOutputSize(length);
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessByte(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ return cipher.ProcessByte(input, output, outOff);
+ }
+
+ public override byte[] ProcessByte(
+ byte input)
+ {
+ int outLength = GetUpdateOutputSize(1);
+
+ byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
+
+ int pos = ProcessByte(input, outBytes, 0);
+
+ if (outLength > 0 && pos < outLength)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+
+ return outBytes;
+ }
+
+ public override byte[] ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (input == null)
+ throw new ArgumentNullException("input");
+ if (length < 1)
+ return null;
+
+ int outLength = GetUpdateOutputSize(length);
+
+ byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
+
+ int pos = ProcessBytes(input, inOff, length, outBytes, 0);
+
+ if (outLength > 0 && pos < outLength)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+
+ return outBytes;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ return cipher.ProcessBytes(input, inOff, length, output, outOff);
+ }
+
+ public override byte[] DoFinal()
+ {
+ byte[] outBytes = EmptyBuffer;
+
+ int length = GetOutputSize(0);
+ if (length > 0)
+ {
+ outBytes = new byte[length];
+
+ int pos = DoFinal(outBytes, 0);
+ if (pos < outBytes.Length)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+ }
+
+ return outBytes;
+ }
+
+ public override byte[] DoFinal(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ if (input == null)
+ throw new ArgumentNullException("input");
+
+ int length = GetOutputSize(inLen);
+
+ byte[] outBytes = EmptyBuffer;
+
+ if (length > 0)
+ {
+ outBytes = new byte[length];
+
+ int pos = (inLen > 0)
+ ? ProcessBytes(input, inOff, inLen, outBytes, 0)
+ : 0;
+
+ pos += DoFinal(outBytes, pos);
+
+ if (pos < outBytes.Length)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+ }
+
+ return outBytes;
+ }
+
+ /**
+ * Process the last block in the buffer.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output, or the input is not block size aligned and should be.
+ * @exception InvalidOperationException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if padding is expected and not found.
+ * @exception DataLengthException if the input is not block size
+ * aligned.
+ */
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ return cipher.DoFinal(output, outOff);
+ }
+
+ /**
+ * Reset the buffer and cipher. After resetting the object is in the same
+ * state as it was after the last init (if there was one).
+ */
+ public override void Reset()
+ {
+ cipher.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/BufferedAsymmetricBlockCipher.cs b/Crypto/src/crypto/BufferedAsymmetricBlockCipher.cs
new file mode 100644
index 000000000..09ec59f69
--- /dev/null
+++ b/Crypto/src/crypto/BufferedAsymmetricBlockCipher.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto.Engines;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * a buffer wrapper for an asymmetric block cipher, allowing input
+ * to be accumulated in a piecemeal fashion until final processing.
+ */
+ public class BufferedAsymmetricBlockCipher
+ : BufferedCipherBase
+ {
+ private readonly IAsymmetricBlockCipher cipher;
+
+ private byte[] buffer;
+ private int bufOff;
+
+ /**
+ * base constructor.
+ *
+ * @param cipher the cipher this buffering object wraps.
+ */
+ public BufferedAsymmetricBlockCipher(
+ IAsymmetricBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ }
+
+ /**
+ * return the amount of data sitting in the buffer.
+ *
+ * @return the amount of data sitting in the buffer.
+ */
+ internal int GetBufferPosition()
+ {
+ return bufOff;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return cipher.AlgorithmName; }
+ }
+
+ public override int GetBlockSize()
+ {
+ return cipher.GetInputBlockSize();
+ }
+
+ public override int GetOutputSize(
+ int length)
+ {
+ return cipher.GetOutputBlockSize();
+ }
+
+ public override int GetUpdateOutputSize(
+ int length)
+ {
+ return 0;
+ }
+
+ /**
+ * initialise the buffer and the underlying cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ */
+ public override void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ Reset();
+
+ cipher.Init(forEncryption, parameters);
+
+ //
+ // we allow for an extra byte where people are using their own padding
+ // mechanisms on a raw cipher.
+ //
+ this.buffer = new byte[cipher.GetInputBlockSize() + (forEncryption ? 1 : 0)];
+ this.bufOff = 0;
+ }
+
+ public override byte[] ProcessByte(
+ byte input)
+ {
+ if (bufOff >= buffer.Length)
+ throw new DataLengthException("attempt to process message to long for cipher");
+
+ buffer[bufOff++] = input;
+ return null;
+ }
+
+ public override byte[] ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (length < 1)
+ return null;
+
+ if (input == null)
+ throw new ArgumentNullException("input");
+ if (bufOff + length > buffer.Length)
+ throw new DataLengthException("attempt to process message to long for cipher");
+
+ Array.Copy(input, inOff, buffer, bufOff, length);
+ bufOff += length;
+ return null;
+ }
+
+ /**
+ * process the contents of the buffer using the underlying
+ * cipher.
+ *
+ * @return the result of the encryption/decryption process on the
+ * buffer.
+ * @exception InvalidCipherTextException if we are given a garbage block.
+ */
+ public override byte[] DoFinal()
+ {
+ byte[] outBytes = bufOff > 0
+ ? cipher.ProcessBlock(buffer, 0, bufOff)
+ : EmptyBuffer;
+
+ Reset();
+
+ return outBytes;
+ }
+
+ public override byte[] DoFinal(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ ProcessBytes(input, inOff, length);
+ return DoFinal();
+ }
+
+ /// <summary>Reset the buffer</summary>
+ public override void Reset()
+ {
+ if (buffer != null)
+ {
+ Array.Clear(buffer, 0, buffer.Length);
+ bufOff = 0;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/BufferedBlockCipher.cs b/Crypto/src/crypto/BufferedBlockCipher.cs
new file mode 100644
index 000000000..72bdfed67
--- /dev/null
+++ b/Crypto/src/crypto/BufferedBlockCipher.cs
@@ -0,0 +1,378 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
+ * buffer is full and more data is being added, or on a doFinal.
+ * <p>
+ * Note: in the case where the underlying cipher is either a CFB cipher or an
+ * OFB one the last block may not be a multiple of the block size.
+ * </p>
+ */
+ public class BufferedBlockCipher
+ : BufferedCipherBase
+ {
+ internal byte[] buf;
+ internal int bufOff;
+ internal bool forEncryption;
+ internal IBlockCipher cipher;
+
+ /**
+ * constructor for subclasses
+ */
+ protected BufferedBlockCipher()
+ {
+ }
+
+ /**
+ * Create a buffered block cipher without padding.
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ * false otherwise.
+ */
+ public BufferedBlockCipher(
+ IBlockCipher cipher)
+ {
+ if (cipher == null)
+ throw new ArgumentNullException("cipher");
+
+ this.cipher = cipher;
+ buf = new byte[cipher.GetBlockSize()];
+ bufOff = 0;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return cipher.AlgorithmName; }
+ }
+
+ /**
+ * initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ // Note: This doubles as the Init in the event that this cipher is being used as an IWrapper
+ public override void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom) parameters).Parameters;
+ }
+
+ Reset();
+
+ cipher.Init(forEncryption, parameters);
+ }
+
+ /**
+ * return the blocksize for the underlying cipher.
+ *
+ * @return the blocksize for the underlying cipher.
+ */
+ public override int GetBlockSize()
+ {
+ return cipher.GetBlockSize();
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public override int GetUpdateOutputSize(
+ int length)
+ {
+ int total = length + bufOff;
+ int leftOver = total % buf.Length;
+ return total - leftOver;
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public override int GetOutputSize(
+ int length)
+ {
+ // Note: Can assume IsPartialBlockOkay is true for purposes of this calculation
+ return length + bufOff;
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessByte(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ buf[bufOff++] = input;
+
+ if (bufOff == buf.Length)
+ {
+ if ((outOff + buf.Length) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ bufOff = 0;
+ return cipher.ProcessBlock(buf, 0, output, outOff);
+ }
+
+ return 0;
+ }
+
+ public override byte[] ProcessByte(
+ byte input)
+ {
+ int outLength = GetUpdateOutputSize(1);
+
+ byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
+
+ int pos = ProcessByte(input, outBytes, 0);
+
+ if (outLength > 0 && pos < outLength)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+
+ return outBytes;
+ }
+
+ public override byte[] ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (input == null)
+ throw new ArgumentNullException("input");
+ if (length < 1)
+ return null;
+
+ int outLength = GetUpdateOutputSize(length);
+
+ byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
+
+ int pos = ProcessBytes(input, inOff, length, outBytes, 0);
+
+ if (outLength > 0 && pos < outLength)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+
+ return outBytes;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ if (length < 1)
+ {
+ if (length < 0)
+ throw new ArgumentException("Can't have a negative input length!");
+
+ return 0;
+ }
+
+ int blockSize = GetBlockSize();
+ int outLength = GetUpdateOutputSize(length);
+
+ if (outLength > 0)
+ {
+ if ((outOff + outLength) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.Length - bufOff;
+ if (length > gapLen)
+ {
+ Array.Copy(input, inOff, buf, bufOff, gapLen);
+ resultLen += cipher.ProcessBlock(buf, 0, output, outOff);
+ bufOff = 0;
+ length -= gapLen;
+ inOff += gapLen;
+ while (length > buf.Length)
+ {
+ resultLen += cipher.ProcessBlock(input, inOff, output, outOff + resultLen);
+ length -= blockSize;
+ inOff += blockSize;
+ }
+ }
+ Array.Copy(input, inOff, buf, bufOff, length);
+ bufOff += length;
+ if (bufOff == buf.Length)
+ {
+ resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen);
+ bufOff = 0;
+ }
+ return resultLen;
+ }
+
+ public override byte[] DoFinal()
+ {
+ byte[] outBytes = EmptyBuffer;
+
+ int length = GetOutputSize(0);
+ if (length > 0)
+ {
+ outBytes = new byte[length];
+
+ int pos = DoFinal(outBytes, 0);
+ if (pos < outBytes.Length)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+ }
+ else
+ {
+ Reset();
+ }
+
+ return outBytes;
+ }
+
+ public override byte[] DoFinal(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ if (input == null)
+ throw new ArgumentNullException("input");
+
+ int length = GetOutputSize(inLen);
+
+ byte[] outBytes = EmptyBuffer;
+
+ if (length > 0)
+ {
+ outBytes = new byte[length];
+
+ int pos = (inLen > 0)
+ ? ProcessBytes(input, inOff, inLen, outBytes, 0)
+ : 0;
+
+ pos += DoFinal(outBytes, pos);
+
+ if (pos < outBytes.Length)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+ }
+ else
+ {
+ Reset();
+ }
+
+ return outBytes;
+ }
+
+ /**
+ * Process the last block in the buffer.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output, or the input is not block size aligned and should be.
+ * @exception InvalidOperationException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if padding is expected and not found.
+ * @exception DataLengthException if the input is not block size
+ * aligned.
+ */
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ try
+ {
+ if (bufOff != 0)
+ {
+ if (!cipher.IsPartialBlockOkay)
+ {
+ throw new DataLengthException("data not block size aligned");
+ }
+
+ if (outOff + bufOff > output.Length)
+ {
+ throw new DataLengthException("output buffer too short for DoFinal()");
+ }
+
+ // NB: Can't copy directly, or we may write too much output
+ cipher.ProcessBlock(buf, 0, buf, 0);
+ Array.Copy(buf, 0, output, outOff, bufOff);
+ }
+
+ return bufOff;
+ }
+ finally
+ {
+ Reset();
+ }
+ }
+
+ /**
+ * Reset the buffer and cipher. After resetting the object is in the same
+ * state as it was after the last init (if there was one).
+ */
+ public override void Reset()
+ {
+ Array.Clear(buf, 0, buf.Length);
+ bufOff = 0;
+
+ cipher.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/BufferedCipherBase.cs b/Crypto/src/crypto/BufferedCipherBase.cs
new file mode 100644
index 000000000..9d8610211
--- /dev/null
+++ b/Crypto/src/crypto/BufferedCipherBase.cs
@@ -0,0 +1,113 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ public abstract class BufferedCipherBase
+ : IBufferedCipher
+ {
+ protected static readonly byte[] EmptyBuffer = new byte[0];
+
+ public abstract string AlgorithmName { get; }
+
+ public abstract void Init(bool forEncryption, ICipherParameters parameters);
+
+ public abstract int GetBlockSize();
+
+ public abstract int GetOutputSize(int inputLen);
+ public abstract int GetUpdateOutputSize(int inputLen);
+
+ public abstract byte[] ProcessByte(byte input);
+
+ public virtual int ProcessByte(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ byte[] outBytes = ProcessByte(input);
+ if (outBytes == null)
+ return 0;
+ if (outOff + outBytes.Length > output.Length)
+ throw new DataLengthException("output buffer too short");
+ outBytes.CopyTo(output, outOff);
+ return outBytes.Length;
+ }
+
+ public virtual byte[] ProcessBytes(
+ byte[] input)
+ {
+ return ProcessBytes(input, 0, input.Length);
+ }
+
+ public abstract byte[] ProcessBytes(byte[] input, int inOff, int length);
+
+ public virtual int ProcessBytes(
+ byte[] input,
+ byte[] output,
+ int outOff)
+ {
+ return ProcessBytes(input, 0, input.Length, output, outOff);
+ }
+
+ public virtual int ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ byte[] outBytes = ProcessBytes(input, inOff, length);
+ if (outBytes == null)
+ return 0;
+ if (outOff + outBytes.Length > output.Length)
+ throw new DataLengthException("output buffer too short");
+ outBytes.CopyTo(output, outOff);
+ return outBytes.Length;
+ }
+
+ public abstract byte[] DoFinal();
+
+ public virtual byte[] DoFinal(
+ byte[] input)
+ {
+ return DoFinal(input, 0, input.Length);
+ }
+
+ public abstract byte[] DoFinal(
+ byte[] input,
+ int inOff,
+ int length);
+
+ public virtual int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ byte[] outBytes = DoFinal();
+ if (outOff + outBytes.Length > output.Length)
+ throw new DataLengthException("output buffer too short");
+ outBytes.CopyTo(output, outOff);
+ return outBytes.Length;
+ }
+
+ public virtual int DoFinal(
+ byte[] input,
+ byte[] output,
+ int outOff)
+ {
+ return DoFinal(input, 0, input.Length, output, outOff);
+ }
+
+ public virtual int DoFinal(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ int len = ProcessBytes(input, inOff, length, output, outOff);
+ len += DoFinal(output, outOff + len);
+ return len;
+ }
+
+ public abstract void Reset();
+ }
+}
diff --git a/Crypto/src/crypto/BufferedIesCipher.cs b/Crypto/src/crypto/BufferedIesCipher.cs
new file mode 100644
index 000000000..6dab4ae33
--- /dev/null
+++ b/Crypto/src/crypto/BufferedIesCipher.cs
@@ -0,0 +1,113 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto
+{
+ public class BufferedIesCipher
+ : BufferedCipherBase
+ {
+ private readonly IesEngine engine;
+ private bool forEncryption;
+ private MemoryStream buffer = new MemoryStream();
+
+ public BufferedIesCipher(
+ IesEngine engine)
+ {
+ if (engine == null)
+ throw new ArgumentNullException("engine");
+
+ this.engine = engine;
+ }
+
+ public override string AlgorithmName
+ {
+ // TODO Create IESEngine.AlgorithmName
+ get { return "IES"; }
+ }
+
+ public override void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+
+ // TODO
+ throw Platform.CreateNotImplementedException("IES");
+ }
+
+ public override int GetBlockSize()
+ {
+ return 0;
+ }
+
+ public override int GetOutputSize(
+ int inputLen)
+ {
+ if (engine == null)
+ throw new InvalidOperationException("cipher not initialised");
+
+ int baseLen = inputLen + (int) buffer.Length;
+ return forEncryption
+ ? baseLen + 20
+ : baseLen - 20;
+ }
+
+ public override int GetUpdateOutputSize(
+ int inputLen)
+ {
+ return 0;
+ }
+
+ public override byte[] ProcessByte(
+ byte input)
+ {
+ buffer.WriteByte(input);
+ return null;
+ }
+
+ public override byte[] ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (input == null)
+ throw new ArgumentNullException("input");
+ if (inOff < 0)
+ throw new ArgumentException("inOff");
+ if (length < 0)
+ throw new ArgumentException("length");
+ if (inOff + length > input.Length)
+ throw new ArgumentException("invalid offset/length specified for input array");
+
+ buffer.Write(input, inOff, length);
+ return null;
+ }
+
+ public override byte[] DoFinal()
+ {
+ byte[] buf = buffer.ToArray();
+
+ Reset();
+
+ return engine.ProcessBlock(buf, 0, buf.Length);
+ }
+
+ public override byte[] DoFinal(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ ProcessBytes(input, inOff, length);
+ return DoFinal();
+ }
+
+ public override void Reset()
+ {
+ buffer.SetLength(0);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/BufferedStreamCipher.cs b/Crypto/src/crypto/BufferedStreamCipher.cs
new file mode 100644
index 000000000..2d4987bba
--- /dev/null
+++ b/Crypto/src/crypto/BufferedStreamCipher.cs
@@ -0,0 +1,131 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto
+{
+ public class BufferedStreamCipher
+ : BufferedCipherBase
+ {
+ private readonly IStreamCipher cipher;
+
+ public BufferedStreamCipher(
+ IStreamCipher cipher)
+ {
+ if (cipher == null)
+ throw new ArgumentNullException("cipher");
+
+ this.cipher = cipher;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return cipher.AlgorithmName; }
+ }
+
+ public override void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom) parameters).Parameters;
+ }
+
+ cipher.Init(forEncryption, parameters);
+ }
+
+ public override int GetBlockSize()
+ {
+ return 0;
+ }
+
+ public override int GetOutputSize(
+ int inputLen)
+ {
+ return inputLen;
+ }
+
+ public override int GetUpdateOutputSize(
+ int inputLen)
+ {
+ return inputLen;
+ }
+
+ public override byte[] ProcessByte(
+ byte input)
+ {
+ return new byte[]{ cipher.ReturnByte(input) };
+ }
+
+ public override int ProcessByte(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ if (outOff >= output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ output[outOff] = cipher.ReturnByte(input);
+ return 1;
+ }
+
+ public override byte[] ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (length < 1)
+ return null;
+
+ byte[] output = new byte[length];
+ cipher.ProcessBytes(input, inOff, length, output, 0);
+ return output;
+ }
+
+ public override int ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ if (length < 1)
+ return 0;
+
+ if (length > 0)
+ {
+ cipher.ProcessBytes(input, inOff, length, output, outOff);
+ }
+
+ return length;
+ }
+
+ public override byte[] DoFinal()
+ {
+ Reset();
+
+ return EmptyBuffer;
+ }
+
+ public override byte[] DoFinal(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (length < 1)
+ return EmptyBuffer;
+
+ byte[] output = ProcessBytes(input, inOff, length);
+
+ Reset();
+
+ return output;
+ }
+
+ public override void Reset()
+ {
+ cipher.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/CipherKeyGenerator.cs b/Crypto/src/crypto/CipherKeyGenerator.cs
new file mode 100644
index 000000000..5d00d34dd
--- /dev/null
+++ b/Crypto/src/crypto/CipherKeyGenerator.cs
@@ -0,0 +1,83 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * The base class for symmetric, or secret, cipher key generators.
+ */
+ public class CipherKeyGenerator
+ {
+ protected internal SecureRandom random;
+ protected internal int strength;
+ private bool uninitialised = true;
+ private int defaultStrength;
+
+ public CipherKeyGenerator()
+ {
+ }
+
+ internal CipherKeyGenerator(
+ int defaultStrength)
+ {
+ if (defaultStrength < 1)
+ throw new ArgumentException("strength must be a positive value", "defaultStrength");
+
+ this.defaultStrength = defaultStrength;
+ }
+
+ public int DefaultStrength
+ {
+ get { return defaultStrength; }
+ }
+
+ /**
+ * initialise the key generator.
+ *
+ * @param param the parameters to be used for key generation
+ */
+ public void Init(
+ KeyGenerationParameters parameters)
+ {
+ if (parameters == null)
+ throw new ArgumentNullException("parameters");
+
+ this.uninitialised = false;
+
+ engineInit(parameters);
+ }
+
+ protected virtual void engineInit(
+ KeyGenerationParameters parameters)
+ {
+ this.random = parameters.Random;
+ this.strength = (parameters.Strength + 7) / 8;
+ }
+
+ /**
+ * Generate a secret key.
+ *
+ * @return a byte array containing the key value.
+ */
+ public byte[] GenerateKey()
+ {
+ if (uninitialised)
+ {
+ if (defaultStrength < 1)
+ throw new InvalidOperationException("Generator has not been initialised");
+
+ uninitialised = false;
+
+ engineInit(new KeyGenerationParameters(new SecureRandom(), defaultStrength));
+ }
+
+ return engineGenerateKey();
+ }
+
+ protected virtual byte[] engineGenerateKey()
+ {
+ return random.GenerateSeed(strength);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/CryptoException.cs b/Crypto/src/crypto/CryptoException.cs
new file mode 100644
index 000000000..c012063bb
--- /dev/null
+++ b/Crypto/src/crypto/CryptoException.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ public class CryptoException
+ : Exception
+ {
+ public CryptoException()
+ {
+ }
+
+ public CryptoException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public CryptoException(
+ string message,
+ Exception exception)
+ : base(message, exception)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/DataLengthException.cs b/Crypto/src/crypto/DataLengthException.cs
new file mode 100644
index 000000000..8bd695bbc
--- /dev/null
+++ b/Crypto/src/crypto/DataLengthException.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * this exception is thrown if a buffer that is meant to have output
+ * copied into it turns out to be too short, or if we've been given
+ * insufficient input. In general this exception will Get thrown rather
+ * than an ArrayOutOfBounds exception.
+ */
+ public class DataLengthException
+ : CryptoException
+ {
+ /**
+ * base constructor.
+ */
+ public DataLengthException()
+ {
+ }
+
+ /**
+ * create a DataLengthException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public DataLengthException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public DataLengthException(
+ string message,
+ Exception exception)
+ : base(message, exception)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/IAsymmetricBlockCipher.cs b/Crypto/src/crypto/IAsymmetricBlockCipher.cs
new file mode 100644
index 000000000..455cfaa69
--- /dev/null
+++ b/Crypto/src/crypto/IAsymmetricBlockCipher.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /// <remarks>Base interface for a public/private key block cipher.</remarks>
+ public interface IAsymmetricBlockCipher
+ {
+ /// <summary>The name of the algorithm this cipher implements.</summary>
+ string AlgorithmName { get; }
+
+ /// <summary>Initialise the cipher.</summary>
+ /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
+ /// <param name="parameters">The key or other data required by the cipher.</param>
+ void Init(bool forEncryption, ICipherParameters parameters);
+
+ /// <returns>The maximum size, in bytes, an input block may be.</returns>
+ int GetInputBlockSize();
+
+ /// <returns>The maximum size, in bytes, an output block will be.</returns>
+ int GetOutputBlockSize();
+
+ /// <summary>Process a block.</summary>
+ /// <param name="inBuf">The input buffer.</param>
+ /// <param name="inOff">The offset into <paramref>inBuf</paramref> that the input block begins.</param>
+ /// <param name="inLen">The length of the input block.</param>
+ /// <exception cref="InvalidCipherTextException">Input decrypts improperly.</exception>
+ /// <exception cref="DataLengthException">Input is too large for the cipher.</exception>
+ byte[] ProcessBlock(byte[] inBuf, int inOff, int inLen);
+ }
+}
diff --git a/Crypto/src/crypto/IAsymmetricCipherKeyPairGenerator.cs b/Crypto/src/crypto/IAsymmetricCipherKeyPairGenerator.cs
new file mode 100644
index 000000000..9ec5dfada
--- /dev/null
+++ b/Crypto/src/crypto/IAsymmetricCipherKeyPairGenerator.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * interface that a public/private key pair generator should conform to.
+ */
+ public interface IAsymmetricCipherKeyPairGenerator
+ {
+ /**
+ * intialise the key pair generator.
+ *
+ * @param the parameters the key pair is to be initialised with.
+ */
+ void Init(KeyGenerationParameters parameters);
+
+ /**
+ * return an AsymmetricCipherKeyPair containing the Generated keys.
+ *
+ * @return an AsymmetricCipherKeyPair containing the Generated keys.
+ */
+ AsymmetricCipherKeyPair GenerateKeyPair();
+ }
+}
diff --git a/Crypto/src/crypto/IBasicAgreement.cs b/Crypto/src/crypto/IBasicAgreement.cs
new file mode 100644
index 000000000..8bd363d4e
--- /dev/null
+++ b/Crypto/src/crypto/IBasicAgreement.cs
@@ -0,0 +1,24 @@
+using System;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * The basic interface that basic Diffie-Hellman implementations
+ * conforms to.
+ */
+ public interface IBasicAgreement
+ {
+ /**
+ * initialise the agreement engine.
+ */
+ void Init(ICipherParameters parameters);
+
+ /**
+ * given a public key from a given party calculate the next
+ * message in the agreement sequence.
+ */
+ BigInteger CalculateAgreement(ICipherParameters pubKey);
+ }
+
+}
diff --git a/Crypto/src/crypto/IBlockCipher.cs b/Crypto/src/crypto/IBlockCipher.cs
new file mode 100644
index 000000000..a3ad6d6e5
--- /dev/null
+++ b/Crypto/src/crypto/IBlockCipher.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /// <remarks>Base interface for a symmetric key block cipher.</remarks>
+ public interface IBlockCipher
+ {
+ /// <summary>The name of the algorithm this cipher implements.</summary>
+ string AlgorithmName { get; }
+
+ /// <summary>Initialise the cipher.</summary>
+ /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
+ /// <param name="parameters">The key or other data required by the cipher.</param>
+ void Init(bool forEncryption, ICipherParameters parameters);
+
+ /// <returns>The block size for this cipher, in bytes.</returns>
+ int GetBlockSize();
+
+ /// <summary>Indicates whether this cipher can handle partial blocks.</summary>
+ bool IsPartialBlockOkay { get; }
+
+ /// <summary>Process a block.</summary>
+ /// <param name="inBuf">The input buffer.</param>
+ /// <param name="inOff">The offset into <paramref>inBuf</paramref> that the input block begins.</param>
+ /// <param name="outBuf">The output buffer.</param>
+ /// <param name="outOff">The offset into <paramref>outBuf</paramref> to write the output block.</param>
+ /// <exception cref="DataLengthException">If input block is wrong size, or outBuf too small.</exception>
+ /// <returns>The number of bytes processed and produced.</returns>
+ int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff);
+
+ /// <summary>
+ /// Reset the cipher to the same state as it was after the last init (if there was one).
+ /// </summary>
+ void Reset();
+ }
+}
diff --git a/Crypto/src/crypto/IBufferedCipher.cs b/Crypto/src/crypto/IBufferedCipher.cs
new file mode 100644
index 000000000..69dec9596
--- /dev/null
+++ b/Crypto/src/crypto/IBufferedCipher.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /// <remarks>Block cipher engines are expected to conform to this interface.</remarks>
+ public interface IBufferedCipher
+ {
+ /// <summary>The name of the algorithm this cipher implements.</summary>
+ string AlgorithmName { get; }
+
+ /// <summary>Initialise the cipher.</summary>
+ /// <param name="forEncryption">If true the cipher is initialised for encryption,
+ /// if false for decryption.</param>
+ /// <param name="parameters">The key and other data required by the cipher.</param>
+ void Init(bool forEncryption, ICipherParameters parameters);
+
+ int GetBlockSize();
+
+ int GetOutputSize(int inputLen);
+
+ int GetUpdateOutputSize(int inputLen);
+
+ byte[] ProcessByte(byte input);
+ int ProcessByte(byte input, byte[] output, int outOff);
+
+ byte[] ProcessBytes(byte[] input);
+ byte[] ProcessBytes(byte[] input, int inOff, int length);
+ int ProcessBytes(byte[] input, byte[] output, int outOff);
+ int ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff);
+
+ byte[] DoFinal();
+ byte[] DoFinal(byte[] input);
+ byte[] DoFinal(byte[] input, int inOff, int length);
+ int DoFinal(byte[] output, int outOff);
+ int DoFinal(byte[] input, byte[] output, int outOff);
+ int DoFinal(byte[] input, int inOff, int length, byte[] output, int outOff);
+
+ /// <summary>
+ /// Reset the cipher. After resetting the cipher is in the same state
+ /// as it was after the last init (if there was one).
+ /// </summary>
+ void Reset();
+ }
+}
diff --git a/Crypto/src/crypto/ICipherParameters.cs b/Crypto/src/crypto/ICipherParameters.cs
new file mode 100644
index 000000000..fff0941c7
--- /dev/null
+++ b/Crypto/src/crypto/ICipherParameters.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * all parameter classes implement this.
+ */
+ public interface ICipherParameters
+ {
+ }
+}
diff --git a/Crypto/src/crypto/IDSA.cs b/Crypto/src/crypto/IDSA.cs
new file mode 100644
index 000000000..46056d8ca
--- /dev/null
+++ b/Crypto/src/crypto/IDSA.cs
@@ -0,0 +1,40 @@
+using System;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * interface for classes implementing the Digital Signature Algorithm
+ */
+ public interface IDsa
+ {
+ string AlgorithmName { get; }
+
+ /**
+ * initialise the signer for signature generation or signature
+ * verification.
+ *
+ * @param forSigning true if we are generating a signature, false
+ * otherwise.
+ * @param param key parameters for signature generation.
+ */
+ void Init(bool forSigning, ICipherParameters parameters);
+
+ /**
+ * sign the passed in message (usually the output of a hash function).
+ *
+ * @param message the message to be signed.
+ * @return two big integers representing the r and s values respectively.
+ */
+ BigInteger[] GenerateSignature(byte[] message);
+
+ /**
+ * verify the message message against the signature values r and s.
+ *
+ * @param message the message that was supposed to have been signed.
+ * @param r the r signature value.
+ * @param s the s signature value.
+ */
+ bool VerifySignature(byte[] message, BigInteger r, BigInteger s);
+ }
+}
diff --git a/Crypto/src/crypto/IDerivationFunction.cs b/Crypto/src/crypto/IDerivationFunction.cs
new file mode 100644
index 000000000..7f289f790
--- /dev/null
+++ b/Crypto/src/crypto/IDerivationFunction.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * base interface for general purpose byte derivation functions.
+ */
+ public interface IDerivationFunction
+ {
+ void Init(IDerivationParameters parameters);
+
+ /**
+ * return the message digest used as the basis for the function
+ */
+ IDigest Digest
+ {
+ get;
+ }
+
+ int GenerateBytes(byte[] output, int outOff, int length);
+ //throws DataLengthException, ArgumentException;
+ }
+
+}
diff --git a/Crypto/src/crypto/IDerivationParameters.cs b/Crypto/src/crypto/IDerivationParameters.cs
new file mode 100644
index 000000000..f1c848568
--- /dev/null
+++ b/Crypto/src/crypto/IDerivationParameters.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * Parameters for key/byte stream derivation classes
+ */
+ public interface IDerivationParameters
+ {
+ }
+}
diff --git a/Crypto/src/crypto/IDigest.cs b/Crypto/src/crypto/IDigest.cs
new file mode 100644
index 000000000..6769dcc42
--- /dev/null
+++ b/Crypto/src/crypto/IDigest.cs
@@ -0,0 +1,61 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * interface that a message digest conforms to.
+ */
+ public interface IDigest
+ {
+ /**
+ * return the algorithm name
+ *
+ * @return the algorithm name
+ */
+ string AlgorithmName { get; }
+
+ /**
+ * return the size, in bytes, of the digest produced by this message digest.
+ *
+ * @return the size, in bytes, of the digest produced by this message digest.
+ */
+ int GetDigestSize();
+
+ /**
+ * return the size, in bytes, of the internal buffer used by this digest.
+ *
+ * @return the size, in bytes, of the internal buffer used by this digest.
+ */
+ int GetByteLength();
+
+ /**
+ * update the message digest with a single byte.
+ *
+ * @param inByte the input byte to be entered.
+ */
+ void Update(byte input);
+
+ /**
+ * update the message digest with a block of bytes.
+ *
+ * @param input the byte array containing the data.
+ * @param inOff the offset into the byte array where the data starts.
+ * @param len the length of the data.
+ */
+ void BlockUpdate(byte[] input, int inOff, int length);
+
+ /**
+ * Close the digest, producing the final digest value. The doFinal
+ * call leaves the digest reset.
+ *
+ * @param output the array the digest is to be copied into.
+ * @param outOff the offset into the out array the digest is to start at.
+ */
+ int DoFinal(byte[] output, int outOff);
+
+ /**
+ * reset the digest back to it's initial state.
+ */
+ void Reset();
+ }
+}
diff --git a/Crypto/src/crypto/IMac.cs b/Crypto/src/crypto/IMac.cs
new file mode 100644
index 000000000..03a86e8b6
--- /dev/null
+++ b/Crypto/src/crypto/IMac.cs
@@ -0,0 +1,69 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * The base interface for implementations of message authentication codes (MACs).
+ */
+ public interface IMac
+ {
+ /**
+ * Initialise the MAC.
+ *
+ * @param param the key and other data required by the MAC.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ void Init(ICipherParameters parameters);
+
+ /**
+ * Return the name of the algorithm the MAC implements.
+ *
+ * @return the name of the algorithm the MAC implements.
+ */
+ string AlgorithmName { get; }
+
+ /**
+ * Return the block size for this MAC (in bytes).
+ *
+ * @return the block size for this MAC in bytes.
+ */
+ int GetMacSize();
+
+ /**
+ * add a single byte to the mac for processing.
+ *
+ * @param in the byte to be processed.
+ * @exception InvalidOperationException if the MAC is not initialised.
+ */
+ void Update(byte input);
+
+ /**
+ * @param in the array containing the input.
+ * @param inOff the index in the array the data begins at.
+ * @param len the length of the input starting at inOff.
+ * @exception InvalidOperationException if the MAC is not initialised.
+ * @exception DataLengthException if there isn't enough data in in.
+ */
+ void BlockUpdate(byte[] input, int inOff, int len);
+
+ /**
+ * Compute the final stage of the MAC writing the output to the out
+ * parameter.
+ * <p>
+ * doFinal leaves the MAC in the same state it was after the last init.
+ * </p>
+ * @param out the array the MAC is to be output to.
+ * @param outOff the offset into the out buffer the output is to start at.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the MAC is not initialised.
+ */
+ int DoFinal(byte[] output, int outOff);
+
+ /**
+ * Reset the MAC. At the end of resetting the MAC should be in the
+ * in the same state it was after the last init (if there was one).
+ */
+ void Reset();
+ }
+}
diff --git a/Crypto/src/crypto/ISigner.cs b/Crypto/src/crypto/ISigner.cs
new file mode 100644
index 000000000..e03bbf4d3
--- /dev/null
+++ b/Crypto/src/crypto/ISigner.cs
@@ -0,0 +1,50 @@
+
+using System;
+using System.Text;
+
+namespace Org.BouncyCastle.Crypto
+{
+ public interface ISigner
+ {
+ /**
+ * Return the name of the algorithm the signer implements.
+ *
+ * @return the name of the algorithm the signer implements.
+ */
+ string AlgorithmName { get; }
+
+ /**
+ * Initialise the signer for signing or verification.
+ *
+ * @param forSigning true if for signing, false otherwise
+ * @param param necessary parameters.
+ */
+ void Init(bool forSigning, ICipherParameters parameters);
+
+ /**
+ * update the internal digest with the byte b
+ */
+ void Update(byte input);
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ void BlockUpdate(byte[] input, int inOff, int length);
+
+ /**
+ * Generate a signature for the message we've been loaded with using
+ * the key we were initialised with.
+ */
+ byte[] GenerateSignature();
+ /**
+ * return true if the internal state represents the signature described
+ * in the passed in array.
+ */
+ bool VerifySignature(byte[] signature);
+
+ /**
+ * reset the internal state
+ */
+ void Reset();
+ }
+}
diff --git a/Crypto/src/crypto/ISignerWithRecovery.cs b/Crypto/src/crypto/ISignerWithRecovery.cs
new file mode 100644
index 000000000..024f5cef5
--- /dev/null
+++ b/Crypto/src/crypto/ISignerWithRecovery.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Text;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * Signer with message recovery.
+ */
+ public interface ISignerWithRecovery
+ : ISigner
+ {
+ /**
+ * Returns true if the signer has recovered the full message as
+ * part of signature verification.
+ *
+ * @return true if full message recovered.
+ */
+ bool HasFullMessage();
+
+ /**
+ * Returns a reference to what message was recovered (if any).
+ *
+ * @return full/partial message, null if nothing.
+ */
+ byte[] GetRecoveredMessage();
+
+ /**
+ * Perform an update with the recovered message before adding any other data. This must
+ * be the first update method called, and calling it will result in the signer assuming
+ * that further calls to update will include message content past what is recoverable.
+ *
+ * @param signature the signature that we are in the process of verifying.
+ * @throws IllegalStateException
+ */
+ void UpdateWithRecoveredMessage(byte[] signature);
+ }
+}
diff --git a/Crypto/src/crypto/IStreamCipher.cs b/Crypto/src/crypto/IStreamCipher.cs
new file mode 100644
index 000000000..8e575a7e5
--- /dev/null
+++ b/Crypto/src/crypto/IStreamCipher.cs
@@ -0,0 +1,45 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /// <summary>The interface stream ciphers conform to.</summary>
+ public interface IStreamCipher
+ {
+ /// <summary>The name of the algorithm this cipher implements.</summary>
+ string AlgorithmName { get; }
+
+ /// <summary>Initialise the cipher.</summary>
+ /// <param name="forEncryption">If true the cipher is initialised for encryption,
+ /// if false for decryption.</param>
+ /// <param name="parameters">The key and other data required by the cipher.</param>
+ /// <exception cref="ArgumentException">
+ /// If the parameters argument is inappropriate.
+ /// </exception>
+ void Init(bool forEncryption, ICipherParameters parameters);
+
+ /// <summary>encrypt/decrypt a single byte returning the result.</summary>
+ /// <param name="input">the byte to be processed.</param>
+ /// <returns>the result of processing the input byte.</returns>
+ byte ReturnByte(byte input);
+
+ /// <summary>
+ /// Process a block of bytes from <c>input</c> putting the result into <c>output</c>.
+ /// </summary>
+ /// <param name="input">The input byte array.</param>
+ /// <param name="inOff">
+ /// The offset into <c>input</c> where the data to be processed starts.
+ /// </param>
+ /// <param name="length">The number of bytes to be processed.</param>
+ /// <param name="output">The output buffer the processed bytes go into.</param>
+ /// <param name="outOff">
+ /// The offset into <c>output</c> the processed data starts at.
+ /// </param>
+ /// <exception cref="DataLengthException">If the output buffer is too small.</exception>
+ void ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff);
+
+ /// <summary>
+ /// Reset the cipher to the same state as it was after the last init (if there was one).
+ /// </summary>
+ void Reset();
+ }
+}
diff --git a/Crypto/src/crypto/IWrapper.cs b/Crypto/src/crypto/IWrapper.cs
new file mode 100644
index 000000000..58202b302
--- /dev/null
+++ b/Crypto/src/crypto/IWrapper.cs
@@ -0,0 +1,18 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto
+{
+ public interface IWrapper
+ {
+ /// <summary>The name of the algorithm this cipher implements.</summary>
+ string AlgorithmName { get; }
+
+ void Init(bool forWrapping, ICipherParameters parameters);
+
+ byte[] Wrap(byte[] input, int inOff, int length);
+
+ byte[] Unwrap(byte[] input, int inOff, int length);
+ }
+}
diff --git a/Crypto/src/crypto/InvalidCipherTextException.cs b/Crypto/src/crypto/InvalidCipherTextException.cs
new file mode 100644
index 000000000..598ea278d
--- /dev/null
+++ b/Crypto/src/crypto/InvalidCipherTextException.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * this exception is thrown whenever we find something we don't expect in a
+ * message.
+ */
+ public class InvalidCipherTextException
+ : CryptoException
+ {
+ /**
+ * base constructor.
+ */
+ public InvalidCipherTextException()
+ {
+ }
+
+ /**
+ * create a InvalidCipherTextException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public InvalidCipherTextException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public InvalidCipherTextException(
+ string message,
+ Exception exception)
+ : base(message, exception)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/KeyGenerationParameters.cs b/Crypto/src/crypto/KeyGenerationParameters.cs
new file mode 100644
index 000000000..0cb6b07c7
--- /dev/null
+++ b/Crypto/src/crypto/KeyGenerationParameters.cs
@@ -0,0 +1,55 @@
+using System;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * The base class for parameters to key generators.
+ */
+ public class KeyGenerationParameters
+ {
+ private SecureRandom random;
+ private int strength;
+
+ /**
+ * initialise the generator with a source of randomness
+ * and a strength (in bits).
+ *
+ * @param random the random byte source.
+ * @param strength the size, in bits, of the keys we want to produce.
+ */
+ public KeyGenerationParameters(
+ SecureRandom random,
+ int strength)
+ {
+ if (random == null)
+ throw new ArgumentNullException("random");
+ if (strength < 1)
+ throw new ArgumentException("strength must be a positive value", "strength");
+
+ this.random = random;
+ this.strength = strength;
+ }
+
+ /**
+ * return the random source associated with this
+ * generator.
+ *
+ * @return the generators random source.
+ */
+ public SecureRandom Random
+ {
+ get { return random; }
+ }
+
+ /**
+ * return the bit strength for keys produced by this generator,
+ *
+ * @return the strength of the keys this generator produces (in bits).
+ */
+ public int Strength
+ {
+ get { return strength; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/MaxBytesExceededException.cs b/Crypto/src/crypto/MaxBytesExceededException.cs
new file mode 100644
index 000000000..9fa28abb0
--- /dev/null
+++ b/Crypto/src/crypto/MaxBytesExceededException.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /// <summary>
+ /// This exception is thrown whenever a cipher requires a change of key, iv
+ /// or similar after x amount of bytes enciphered
+ /// </summary>
+ public class MaxBytesExceededException
+ : CryptoException
+ {
+ public MaxBytesExceededException()
+ {
+ }
+
+ public MaxBytesExceededException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public MaxBytesExceededException(
+ string message,
+ Exception e)
+ : base(message, e)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/PbeParametersGenerator.cs b/Crypto/src/crypto/PbeParametersGenerator.cs
new file mode 100644
index 000000000..0e96abd0f
--- /dev/null
+++ b/Crypto/src/crypto/PbeParametersGenerator.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * super class for all Password Based Encyrption (Pbe) parameter generator classes.
+ */
+ public abstract class PbeParametersGenerator
+ {
+ protected byte[] mPassword;
+ protected byte[] mSalt;
+ protected int mIterationCount;
+
+ /**
+ * base constructor.
+ */
+ protected PbeParametersGenerator()
+ {
+ }
+
+ /**
+ * initialise the Pbe generator.
+ *
+ * @param password the password converted into bytes (see below).
+ * @param salt the salt to be mixed with the password.
+ * @param iterationCount the number of iterations the "mixing" function
+ * is to be applied for.
+ */
+ public virtual void Init(
+ byte[] password,
+ byte[] salt,
+ int iterationCount)
+ {
+ if (password == null)
+ throw new ArgumentNullException("password");
+ if (salt == null)
+ throw new ArgumentNullException("salt");
+
+ this.mPassword = Arrays.Clone(password);
+ this.mSalt = Arrays.Clone(salt);
+ this.mIterationCount = iterationCount;
+ }
+
+ public virtual byte[] Password
+ {
+ get { return Arrays.Clone(mPassword); }
+ }
+
+ /**
+ * return the password byte array.
+ *
+ * @return the password byte array.
+ */
+ [Obsolete("Use 'Password' property")]
+ public byte[] GetPassword()
+ {
+ return Password;
+ }
+
+ public virtual byte[] Salt
+ {
+ get { return Arrays.Clone(mSalt); }
+ }
+
+ /**
+ * return the salt byte array.
+ *
+ * @return the salt byte array.
+ */
+ [Obsolete("Use 'Salt' property")]
+ public byte[] GetSalt()
+ {
+ return Salt;
+ }
+
+ /**
+ * return the iteration count.
+ *
+ * @return the iteration count.
+ */
+ public virtual int IterationCount
+ {
+ get { return mIterationCount; }
+ }
+
+ /**
+ * Generate derived parameters for a key of length keySize.
+ *
+ * @param keySize the length, in bits, of the key required.
+ * @return a parameters object representing a key.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public abstract ICipherParameters GenerateDerivedParameters(int keySize);
+ public abstract ICipherParameters GenerateDerivedParameters(string algorithm, int keySize);
+
+ /**
+ * Generate derived parameters for a key of length keySize, and
+ * an initialisation vector (IV) of length ivSize.
+ *
+ * @param keySize the length, in bits, of the key required.
+ * @param ivSize the length, in bits, of the iv required.
+ * @return a parameters object representing a key and an IV.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public abstract ICipherParameters GenerateDerivedParameters(int keySize, int ivSize);
+ public abstract ICipherParameters GenerateDerivedParameters(string algorithm, int keySize, int ivSize);
+
+ /**
+ * Generate derived parameters for a key of length keySize, specifically
+ * for use with a MAC.
+ *
+ * @param keySize the length, in bits, of the key required.
+ * @return a parameters object representing a key.
+ */
+ public abstract ICipherParameters GenerateDerivedMacParameters(int keySize);
+
+ /**
+ * converts a password to a byte array according to the scheme in
+ * Pkcs5 (ascii, no padding)
+ *
+ * @param password a character array representing the password.
+ * @return a byte array representing the password.
+ */
+ public static byte[] Pkcs5PasswordToBytes(
+ char[] password)
+ {
+ return Strings.ToAsciiByteArray(password);
+ }
+
+ [Obsolete("Use version taking 'char[]' instead")]
+ public static byte[] Pkcs5PasswordToBytes(
+ string password)
+ {
+ return Strings.ToAsciiByteArray(password);
+ }
+
+ /**
+ * converts a password to a byte array according to the scheme in
+ * PKCS5 (UTF-8, no padding)
+ *
+ * @param password a character array representing the password.
+ * @return a byte array representing the password.
+ */
+ public static byte[] Pkcs5PasswordToUtf8Bytes(
+ char[] password)
+ {
+ return Encoding.UTF8.GetBytes(password);
+ }
+
+ [Obsolete("Use version taking 'char[]' instead")]
+ public static byte[] Pkcs5PasswordToUtf8Bytes(
+ string password)
+ {
+ return Encoding.UTF8.GetBytes(password);
+ }
+
+ /**
+ * converts a password to a byte array according to the scheme in
+ * Pkcs12 (unicode, big endian, 2 zero pad bytes at the end).
+ *
+ * @param password a character array representing the password.
+ * @return a byte array representing the password.
+ */
+ public static byte[] Pkcs12PasswordToBytes(
+ char[] password)
+ {
+ return Pkcs12PasswordToBytes(password, false);
+ }
+
+ public static byte[] Pkcs12PasswordToBytes(
+ char[] password,
+ bool wrongPkcs12Zero)
+ {
+ if (password.Length < 1)
+ {
+ return new byte[wrongPkcs12Zero ? 2 : 0];
+ }
+
+ // +1 for extra 2 pad bytes.
+ byte[] bytes = new byte[(password.Length + 1) * 2];
+
+ Encoding.BigEndianUnicode.GetBytes(password, 0, password.Length, bytes, 0);
+
+ return bytes;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/StreamBlockCipher.cs b/Crypto/src/crypto/StreamBlockCipher.cs
new file mode 100644
index 000000000..ef2a8b68a
--- /dev/null
+++ b/Crypto/src/crypto/StreamBlockCipher.cs
@@ -0,0 +1,109 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * a wrapper for block ciphers with a single byte block size, so that they
+ * can be treated like stream ciphers.
+ */
+ public class StreamBlockCipher
+ : IStreamCipher
+ {
+ private readonly IBlockCipher cipher;
+ private readonly byte[] oneByte = new byte[1];
+
+ /**
+ * basic constructor.
+ *
+ * @param cipher the block cipher to be wrapped.
+ * @exception ArgumentException if the cipher has a block size other than
+ * one.
+ */
+ public StreamBlockCipher(
+ IBlockCipher cipher)
+ {
+ if (cipher == null)
+ throw new ArgumentNullException("cipher");
+ if (cipher.GetBlockSize() != 1)
+ throw new ArgumentException("block cipher block size != 1.", "cipher");
+
+ this.cipher = cipher;
+ }
+
+ /**
+ * initialise the underlying cipher.
+ *
+ * @param forEncryption true if we are setting up for encryption, false otherwise.
+ * @param param the necessary parameters for the underlying cipher to be initialised.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ cipher.Init(forEncryption, parameters);
+ }
+
+ /**
+ * return the name of the algorithm we are wrapping.
+ *
+ * @return the name of the algorithm we are wrapping.
+ */
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName; }
+ }
+
+ /**
+ * encrypt/decrypt a single byte returning the result.
+ *
+ * @param in the byte to be processed.
+ * @return the result of processing the input byte.
+ */
+ public byte ReturnByte(
+ byte input)
+ {
+ oneByte[0] = input;
+
+ cipher.ProcessBlock(oneByte, 0, oneByte, 0);
+
+ return oneByte[0];
+ }
+
+ /**
+ * process a block of bytes from in putting the result into out.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ * @param out the output buffer the processed bytes go into.
+ * @param outOff the offset into the output byte array the processed data stars at.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ public void ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ if (outOff + length > output.Length)
+ throw new DataLengthException("output buffer too small in ProcessBytes()");
+
+ for (int i = 0; i != length; i++)
+ {
+ cipher.ProcessBlock(input, inOff + i, output, outOff + i);
+ }
+ }
+
+ /**
+ * reset the underlying cipher. This leaves it in the same state
+ * it was at after the last init (if there was one).
+ */
+ public void Reset()
+ {
+ cipher.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/DHAgreement.cs b/Crypto/src/crypto/agreement/DHAgreement.cs
new file mode 100644
index 000000000..d214caafe
--- /dev/null
+++ b/Crypto/src/crypto/agreement/DHAgreement.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ /**
+ * a Diffie-Hellman key exchange engine.
+ * <p>
+ * note: This uses MTI/A0 key agreement in order to make the key agreement
+ * secure against passive attacks. If you're doing Diffie-Hellman and both
+ * parties have long term public keys you should look at using this. For
+ * further information have a look at RFC 2631.</p>
+ * <p>
+ * It's possible to extend this to more than two parties as well, for the moment
+ * that is left as an exercise for the reader.</p>
+ */
+ public class DHAgreement
+ {
+ private DHPrivateKeyParameters key;
+ private DHParameters dhParams;
+ private BigInteger privateValue;
+ private SecureRandom random;
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ AsymmetricKeyParameter kParam;
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+
+ this.random = rParam.Random;
+ kParam = (AsymmetricKeyParameter)rParam.Parameters;
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ kParam = (AsymmetricKeyParameter)parameters;
+ }
+
+ if (!(kParam is DHPrivateKeyParameters))
+ {
+ throw new ArgumentException("DHEngine expects DHPrivateKeyParameters");
+ }
+
+ this.key = (DHPrivateKeyParameters)kParam;
+ this.dhParams = key.Parameters;
+ }
+
+ /**
+ * calculate our initial message.
+ */
+ public BigInteger CalculateMessage()
+ {
+ DHKeyPairGenerator dhGen = new DHKeyPairGenerator();
+ dhGen.Init(new DHKeyGenerationParameters(random, dhParams));
+ AsymmetricCipherKeyPair dhPair = dhGen.GenerateKeyPair();
+
+ this.privateValue = ((DHPrivateKeyParameters)dhPair.Private).X;
+
+ return ((DHPublicKeyParameters)dhPair.Public).Y;
+ }
+
+ /**
+ * given a message from a given party and the corresponding public key
+ * calculate the next message in the agreement sequence. In this case
+ * this will represent the shared secret.
+ */
+ public BigInteger CalculateAgreement(
+ DHPublicKeyParameters pub,
+ BigInteger message)
+ {
+ if (pub == null)
+ throw new ArgumentNullException("pub");
+ if (message == null)
+ throw new ArgumentNullException("message");
+
+ if (!pub.Parameters.Equals(dhParams))
+ {
+ throw new ArgumentException("Diffie-Hellman public key has wrong parameters.");
+ }
+
+ BigInteger p = dhParams.P;
+
+ return message.ModPow(key.X, p).Multiply(pub.Y.ModPow(privateValue, p)).Mod(p);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/DHBasicAgreement.cs b/Crypto/src/crypto/agreement/DHBasicAgreement.cs
new file mode 100644
index 000000000..5a5277049
--- /dev/null
+++ b/Crypto/src/crypto/agreement/DHBasicAgreement.cs
@@ -0,0 +1,60 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ /**
+ * a Diffie-Hellman key agreement class.
+ * <p>
+ * note: This is only the basic algorithm, it doesn't take advantage of
+ * long term public keys if they are available. See the DHAgreement class
+ * for a "better" implementation.</p>
+ */
+ public class DHBasicAgreement
+ : IBasicAgreement
+ {
+ private DHPrivateKeyParameters key;
+ private DHParameters dhParams;
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom) parameters).Parameters;
+ }
+
+ if (!(parameters is DHPrivateKeyParameters))
+ {
+ throw new ArgumentException("DHEngine expects DHPrivateKeyParameters");
+ }
+
+ this.key = (DHPrivateKeyParameters) parameters;
+ this.dhParams = key.Parameters;
+ }
+
+ /**
+ * given a short term public key from a given party calculate the next
+ * message in the agreement sequence.
+ */
+ public BigInteger CalculateAgreement(
+ ICipherParameters pubKey)
+ {
+ if (this.key == null)
+ throw new InvalidOperationException("Agreement algorithm not initialised");
+
+ DHPublicKeyParameters pub = (DHPublicKeyParameters)pubKey;
+
+ if (!pub.Parameters.Equals(dhParams))
+ {
+ throw new ArgumentException("Diffie-Hellman public key has wrong parameters.");
+ }
+
+ return pub.Y.ModPow(key.X, dhParams.P);
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/agreement/ECDHBasicAgreement.cs b/Crypto/src/crypto/agreement/ECDHBasicAgreement.cs
new file mode 100644
index 000000000..f272e4969
--- /dev/null
+++ b/Crypto/src/crypto/agreement/ECDHBasicAgreement.cs
@@ -0,0 +1,50 @@
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ /**
+ * P1363 7.2.1 ECSVDP-DH
+ *
+ * ECSVDP-DH is Elliptic Curve Secret Value Derivation Primitive,
+ * Diffie-Hellman version. It is based on the work of [DH76], [Mil86],
+ * and [Kob87]. This primitive derives a shared secret value from one
+ * party's private key and another party's public key, where both have
+ * the same set of EC domain parameters. If two parties correctly
+ * execute this primitive, they will produce the same output. This
+ * primitive can be invoked by a scheme to derive a shared secret key;
+ * specifically, it may be used with the schemes ECKAS-DH1 and
+ * DL/ECKAS-DH2. It assumes that the input keys are valid (see also
+ * Section 7.2.2).
+ */
+ public class ECDHBasicAgreement
+ : IBasicAgreement
+ {
+ protected internal ECPrivateKeyParameters privKey;
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom)parameters).Parameters;
+ }
+
+ this.privKey = (ECPrivateKeyParameters)parameters;
+ }
+
+ public virtual BigInteger CalculateAgreement(
+ ICipherParameters pubKey)
+ {
+ ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey;
+ ECPoint P = pub.Q.Multiply(privKey.D);
+
+ // if ( p.IsInfinity ) throw new Exception("d*Q == infinity");
+
+ return P.X.ToBigInteger();
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/agreement/ECDHCBasicAgreement.cs b/Crypto/src/crypto/agreement/ECDHCBasicAgreement.cs
new file mode 100644
index 000000000..905d241fc
--- /dev/null
+++ b/Crypto/src/crypto/agreement/ECDHCBasicAgreement.cs
@@ -0,0 +1,58 @@
+using System;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ /**
+ * P1363 7.2.2 ECSVDP-DHC
+ *
+ * ECSVDP-DHC is Elliptic Curve Secret Value Derivation Primitive,
+ * Diffie-Hellman version with cofactor multiplication. It is based on
+ * the work of [DH76], [Mil86], [Kob87], [LMQ98] and [Kal98a]. This
+ * primitive derives a shared secret value from one party's private key
+ * and another party's public key, where both have the same set of EC
+ * domain parameters. If two parties correctly execute this primitive,
+ * they will produce the same output. This primitive can be invoked by a
+ * scheme to derive a shared secret key; specifically, it may be used
+ * with the schemes ECKAS-DH1 and DL/ECKAS-DH2. It does not assume the
+ * validity of the input public key (see also Section 7.2.1).
+ * <p>
+ * Note: As stated P1363 compatibility mode with ECDH can be preset, and
+ * in this case the implementation doesn't have a ECDH compatibility mode
+ * (if you want that just use ECDHBasicAgreement and note they both implement
+ * BasicAgreement!).</p>
+ */
+ public class ECDHCBasicAgreement
+ : IBasicAgreement
+ {
+ private ECPrivateKeyParameters key;
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom) parameters).Parameters;
+ }
+
+ this.key = (ECPrivateKeyParameters)parameters;
+ }
+
+ public BigInteger CalculateAgreement(
+ ICipherParameters pubKey)
+ {
+ ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey;
+ ECDomainParameters parameters = pub.Parameters;
+ ECPoint P = pub.Q.Multiply(parameters.H.Multiply(key.D));
+
+ // if ( p.IsInfinity ) throw new Exception("Invalid public key");
+
+ return P.X.ToBigInteger();
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs b/Crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs
new file mode 100644
index 000000000..28437a268
--- /dev/null
+++ b/Crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.Agreement.Kdf;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ public class ECDHWithKdfBasicAgreement
+ : ECDHBasicAgreement
+ {
+ private readonly string algorithm;
+ private readonly IDerivationFunction kdf;
+
+ public ECDHWithKdfBasicAgreement(
+ string algorithm,
+ IDerivationFunction kdf)
+ {
+ if (algorithm == null)
+ throw new ArgumentNullException("algorithm");
+ if (kdf == null)
+ throw new ArgumentNullException("kdf");
+
+ this.algorithm = algorithm;
+ this.kdf = kdf;
+ }
+
+ public override BigInteger CalculateAgreement(
+ ICipherParameters pubKey)
+ {
+ // Note that the ec.KeyAgreement class in JCE only uses kdf in one
+ // of the engineGenerateSecret methods.
+
+ BigInteger result = base.CalculateAgreement(pubKey);
+
+ int keySize = GeneratorUtilities.GetDefaultKeySize(algorithm);
+
+ DHKdfParameters dhKdfParams = new DHKdfParameters(
+ new DerObjectIdentifier(algorithm),
+ keySize,
+ bigIntToBytes(result));
+
+ kdf.Init(dhKdfParams);
+
+ byte[] keyBytes = new byte[keySize / 8];
+ kdf.GenerateBytes(keyBytes, 0, keyBytes.Length);
+
+ return new BigInteger(1, keyBytes);
+ }
+
+ private byte[] bigIntToBytes(
+ BigInteger r)
+ {
+ int byteLength = X9IntegerConverter.GetByteLength(privKey.Parameters.G.X);
+ return X9IntegerConverter.IntegerToBytes(r, byteLength);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/ECMqvBasicAgreement.cs b/Crypto/src/crypto/agreement/ECMqvBasicAgreement.cs
new file mode 100644
index 000000000..51faa8e49
--- /dev/null
+++ b/Crypto/src/crypto/agreement/ECMqvBasicAgreement.cs
@@ -0,0 +1,85 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ public class ECMqvBasicAgreement
+ : IBasicAgreement
+ {
+ protected internal MqvPrivateParameters privParams;
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom)parameters).Parameters;
+ }
+
+ this.privParams = (MqvPrivateParameters)parameters;
+ }
+
+ public virtual BigInteger CalculateAgreement(
+ ICipherParameters pubKey)
+ {
+ MqvPublicParameters pubParams = (MqvPublicParameters)pubKey;
+
+ ECPrivateKeyParameters staticPrivateKey = privParams.StaticPrivateKey;
+
+ ECPoint agreement = calculateMqvAgreement(staticPrivateKey.Parameters, staticPrivateKey,
+ privParams.EphemeralPrivateKey, privParams.EphemeralPublicKey,
+ pubParams.StaticPublicKey, pubParams.EphemeralPublicKey);
+
+ return agreement.X.ToBigInteger();
+ }
+
+ // The ECMQV Primitive as described in SEC-1, 3.4
+ private static ECPoint calculateMqvAgreement(
+ ECDomainParameters parameters,
+ ECPrivateKeyParameters d1U,
+ ECPrivateKeyParameters d2U,
+ ECPublicKeyParameters Q2U,
+ ECPublicKeyParameters Q1V,
+ ECPublicKeyParameters Q2V)
+ {
+ BigInteger n = parameters.N;
+ int e = (n.BitLength + 1) / 2;
+ BigInteger powE = BigInteger.One.ShiftLeft(e);
+
+ // The Q2U public key is optional
+ ECPoint q;
+ if (Q2U == null)
+ {
+ q = parameters.G.Multiply(d2U.D);
+ }
+ else
+ {
+ q = Q2U.Q;
+ }
+
+ BigInteger x = q.X.ToBigInteger();
+ BigInteger xBar = x.Mod(powE);
+ BigInteger Q2UBar = xBar.SetBit(e);
+ BigInteger s = d1U.D.Multiply(Q2UBar).Mod(n).Add(d2U.D).Mod(n);
+
+ BigInteger xPrime = Q2V.Q.X.ToBigInteger();
+ BigInteger xPrimeBar = xPrime.Mod(powE);
+ BigInteger Q2VBar = xPrimeBar.SetBit(e);
+
+ BigInteger hs = parameters.H.Multiply(s).Mod(n);
+
+ //ECPoint p = Q1V.Q.Multiply(Q2VBar).Add(Q2V.Q).Multiply(hs);
+ ECPoint p = ECAlgorithms.SumOfTwoMultiplies(
+ Q1V.Q, Q2VBar.Multiply(hs).Mod(n), Q2V.Q, hs);
+
+ if (p.IsInfinity)
+ throw new InvalidOperationException("Infinity is not a valid agreement value for MQV");
+
+ return p;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs b/Crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs
new file mode 100644
index 000000000..093ce4056
--- /dev/null
+++ b/Crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.Agreement.Kdf;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ public class ECMqvWithKdfBasicAgreement
+ : ECMqvBasicAgreement
+ {
+ private readonly string algorithm;
+ private readonly IDerivationFunction kdf;
+
+ public ECMqvWithKdfBasicAgreement(
+ string algorithm,
+ IDerivationFunction kdf)
+ {
+ if (algorithm == null)
+ throw new ArgumentNullException("algorithm");
+ if (kdf == null)
+ throw new ArgumentNullException("kdf");
+
+ this.algorithm = algorithm;
+ this.kdf = kdf;
+ }
+
+ public override BigInteger CalculateAgreement(
+ ICipherParameters pubKey)
+ {
+ // Note that the ec.KeyAgreement class in JCE only uses kdf in one
+ // of the engineGenerateSecret methods.
+
+ BigInteger result = base.CalculateAgreement(pubKey);
+
+ int keySize = GeneratorUtilities.GetDefaultKeySize(algorithm);
+
+ DHKdfParameters dhKdfParams = new DHKdfParameters(
+ new DerObjectIdentifier(algorithm),
+ keySize,
+ bigIntToBytes(result));
+
+ kdf.Init(dhKdfParams);
+
+ byte[] keyBytes = new byte[keySize / 8];
+ kdf.GenerateBytes(keyBytes, 0, keyBytes.Length);
+
+ return new BigInteger(1, keyBytes);
+ }
+
+ private byte[] bigIntToBytes(
+ BigInteger r)
+ {
+ int byteLength = X9IntegerConverter.GetByteLength(privParams.StaticPrivateKey.Parameters.G.X);
+ return X9IntegerConverter.IntegerToBytes(r, byteLength);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/kdf/DHKdfParameters.cs b/Crypto/src/crypto/agreement/kdf/DHKdfParameters.cs
new file mode 100644
index 000000000..f6c9e6079
--- /dev/null
+++ b/Crypto/src/crypto/agreement/kdf/DHKdfParameters.cs
@@ -0,0 +1,57 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Kdf
+{
+ public class DHKdfParameters
+ : IDerivationParameters
+ {
+ private readonly DerObjectIdentifier algorithm;
+ private readonly int keySize;
+ private readonly byte[] z;
+ private readonly byte[] extraInfo;
+
+ public DHKdfParameters(
+ DerObjectIdentifier algorithm,
+ int keySize,
+ byte[] z)
+ : this(algorithm, keySize, z, null)
+ {
+ }
+
+ public DHKdfParameters(
+ DerObjectIdentifier algorithm,
+ int keySize,
+ byte[] z,
+ byte[] extraInfo)
+ {
+ this.algorithm = algorithm;
+ this.keySize = keySize;
+ this.z = z; // TODO Clone?
+ this.extraInfo = extraInfo;
+ }
+
+ public DerObjectIdentifier Algorithm
+ {
+ get { return algorithm; }
+ }
+
+ public int KeySize
+ {
+ get { return keySize; }
+ }
+
+ public byte[] GetZ()
+ {
+ // TODO Clone?
+ return z;
+ }
+
+ public byte[] GetExtraInfo()
+ {
+ // TODO Clone?
+ return extraInfo;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/kdf/DHKekGenerator.cs b/Crypto/src/crypto/agreement/kdf/DHKekGenerator.cs
new file mode 100644
index 000000000..fa2921539
--- /dev/null
+++ b/Crypto/src/crypto/agreement/kdf/DHKekGenerator.cs
@@ -0,0 +1,129 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Kdf
+{
+ /**
+ * RFC 2631 Diffie-hellman KEK derivation function.
+ */
+ public class DHKekGenerator
+ : IDerivationFunction
+ {
+ private readonly IDigest digest;
+
+ private DerObjectIdentifier algorithm;
+ private int keySize;
+ private byte[] z;
+ private byte[] partyAInfo;
+
+ public DHKekGenerator(
+ IDigest digest)
+ {
+ this.digest = digest;
+ }
+
+ public void Init(
+ IDerivationParameters param)
+ {
+ DHKdfParameters parameters = (DHKdfParameters)param;
+
+ this.algorithm = parameters.Algorithm;
+ this.keySize = parameters.KeySize;
+ this.z = parameters.GetZ(); // TODO Clone?
+ this.partyAInfo = parameters.GetExtraInfo(); // TODO Clone?
+ }
+
+ public IDigest Digest
+ {
+ get { return digest; }
+ }
+
+ public int GenerateBytes(
+ byte[] outBytes,
+ int outOff,
+ int len)
+ {
+ if ((outBytes.Length - len) < outOff)
+ {
+ throw new DataLengthException("output buffer too small");
+ }
+
+ long oBytes = len;
+ int outLen = digest.GetDigestSize();
+
+ //
+ // this is at odds with the standard implementation, the
+ // maximum value should be hBits * (2^32 - 1) where hBits
+ // is the digest output size in bits. We can't have an
+ // array with a long index at the moment...
+ //
+ if (oBytes > ((2L << 32) - 1))
+ {
+ throw new ArgumentException("Output length too large");
+ }
+
+ int cThreshold = (int)((oBytes + outLen - 1) / outLen);
+
+ byte[] dig = new byte[digest.GetDigestSize()];
+
+ int counter = 1;
+
+ for (int i = 0; i < cThreshold; i++)
+ {
+ digest.BlockUpdate(z, 0, z.Length);
+
+ // KeySpecificInfo
+ DerSequence keyInfo = new DerSequence(
+ algorithm,
+ new DerOctetString(integerToBytes(counter)));
+
+ // OtherInfo
+ Asn1EncodableVector v1 = new Asn1EncodableVector(keyInfo);
+
+ if (partyAInfo != null)
+ {
+ v1.Add(new DerTaggedObject(true, 0, new DerOctetString(partyAInfo)));
+ }
+
+ v1.Add(new DerTaggedObject(true, 2, new DerOctetString(integerToBytes(keySize))));
+
+ byte[] other = new DerSequence(v1).GetDerEncoded();
+
+ digest.BlockUpdate(other, 0, other.Length);
+
+ digest.DoFinal(dig, 0);
+
+ if (len > outLen)
+ {
+ Array.Copy(dig, 0, outBytes, outOff, outLen);
+ outOff += outLen;
+ len -= outLen;
+ }
+ else
+ {
+ Array.Copy(dig, 0, outBytes, outOff, len);
+ }
+
+ counter++;
+ }
+
+ digest.Reset();
+
+ return len;
+ }
+
+ private byte[] integerToBytes(
+ int keySize)
+ {
+ byte[] val = new byte[4];
+
+ val[0] = (byte)(keySize >> 24);
+ val[1] = (byte)(keySize >> 16);
+ val[2] = (byte)(keySize >> 8);
+ val[3] = (byte)keySize;
+
+ return val;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs b/Crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
new file mode 100644
index 000000000..7d55aa485
--- /dev/null
+++ b/Crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
@@ -0,0 +1,71 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Kdf
+{
+ /**
+ * X9.63 based key derivation function for ECDH CMS.
+ */
+ public class ECDHKekGenerator
+ : IDerivationFunction
+ {
+ private readonly IDerivationFunction kdf;
+
+ private DerObjectIdentifier algorithm;
+ private int keySize;
+ private byte[] z;
+
+ public ECDHKekGenerator(
+ IDigest digest)
+ {
+ this.kdf = new Kdf2BytesGenerator(digest);
+ }
+
+ public void Init(
+ IDerivationParameters param)
+ {
+ DHKdfParameters parameters = (DHKdfParameters)param;
+
+ this.algorithm = parameters.Algorithm;
+ this.keySize = parameters.KeySize;
+ this.z = parameters.GetZ(); // TODO Clone?
+ }
+
+ public IDigest Digest
+ {
+ get { return kdf.Digest; }
+ }
+
+ public int GenerateBytes(
+ byte[] outBytes,
+ int outOff,
+ int len)
+ {
+ // TODO Create an ASN.1 class for this (RFC3278)
+ // ECC-CMS-SharedInfo
+ DerSequence s = new DerSequence(
+ new AlgorithmIdentifier(algorithm, DerNull.Instance),
+ new DerTaggedObject(true, 2, new DerOctetString(integerToBytes(keySize))));
+
+ kdf.Init(new KdfParameters(z, s.GetDerEncoded()));
+
+ return kdf.GenerateBytes(outBytes, outOff, len);
+ }
+
+ private byte[] integerToBytes(int keySize)
+ {
+ byte[] val = new byte[4];
+
+ val[0] = (byte)(keySize >> 24);
+ val[1] = (byte)(keySize >> 16);
+ val[2] = (byte)(keySize >> 8);
+ val[3] = (byte)keySize;
+
+ return val;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/srp/SRP6Client.cs b/Crypto/src/crypto/agreement/srp/SRP6Client.cs
new file mode 100644
index 000000000..309736564
--- /dev/null
+++ b/Crypto/src/crypto/agreement/srp/SRP6Client.cs
@@ -0,0 +1,93 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Srp
+{
+ /**
+ * Implements the client side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
+ * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper
+ * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
+ */
+ public class Srp6Client
+ {
+ protected BigInteger N;
+ protected BigInteger g;
+
+ protected BigInteger privA;
+ protected BigInteger pubA;
+
+ protected BigInteger B;
+
+ protected BigInteger x;
+ protected BigInteger u;
+ protected BigInteger S;
+
+ protected IDigest digest;
+ protected SecureRandom random;
+
+ public Srp6Client()
+ {
+ }
+
+ /**
+ * Initialises the client to begin new authentication attempt
+ * @param N The safe prime associated with the client's verifier
+ * @param g The group parameter associated with the client's verifier
+ * @param digest The digest algorithm associated with the client's verifier
+ * @param random For key generation
+ */
+ public virtual void Init(BigInteger N, BigInteger g, IDigest digest, SecureRandom random)
+ {
+ this.N = N;
+ this.g = g;
+ this.digest = digest;
+ this.random = random;
+ }
+
+ /**
+ * Generates client's credentials given the client's salt, identity and password
+ * @param salt The salt used in the client's verifier.
+ * @param identity The user's identity (eg. username)
+ * @param password The user's password
+ * @return Client's public value to send to server
+ */
+ public virtual BigInteger GenerateClientCredentials(byte[] salt, byte[] identity, byte[] password)
+ {
+ this.x = Srp6Utilities.CalculateX(digest, N, salt, identity, password);
+ this.privA = SelectPrivateValue();
+ this.pubA = g.ModPow(privA, N);
+
+ return pubA;
+ }
+
+ /**
+ * Generates client's verification message given the server's credentials
+ * @param serverB The server's credentials
+ * @return Client's verification message for the server
+ * @throws CryptoException If server's credentials are invalid
+ */
+ public virtual BigInteger CalculateSecret(BigInteger serverB)
+ {
+ this.B = Srp6Utilities.ValidatePublicValue(N, serverB);
+ this.u = Srp6Utilities.CalculateU(digest, N, pubA, B);
+ this.S = CalculateS();
+
+ return S;
+ }
+
+ protected virtual BigInteger SelectPrivateValue()
+ {
+ return Srp6Utilities.GeneratePrivateValue(digest, N, g, random);
+ }
+
+ private BigInteger CalculateS()
+ {
+ BigInteger k = Srp6Utilities.CalculateK(digest, N, g);
+ BigInteger exp = u.Multiply(x).Add(privA);
+ BigInteger tmp = g.ModPow(x, N).Multiply(k).Mod(N);
+ return B.Subtract(tmp).Mod(N).ModPow(exp, N);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/srp/SRP6Server.cs b/Crypto/src/crypto/agreement/srp/SRP6Server.cs
new file mode 100644
index 000000000..35b96d488
--- /dev/null
+++ b/Crypto/src/crypto/agreement/srp/SRP6Server.cs
@@ -0,0 +1,90 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Srp
+{
+ /**
+ * Implements the server side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
+ * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper
+ * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
+ */
+ public class Srp6Server
+ {
+ protected BigInteger N;
+ protected BigInteger g;
+ protected BigInteger v;
+
+ protected SecureRandom random;
+ protected IDigest digest;
+
+ protected BigInteger A;
+
+ protected BigInteger privB;
+ protected BigInteger pubB;
+
+ protected BigInteger u;
+ protected BigInteger S;
+
+ public Srp6Server()
+ {
+ }
+
+ /**
+ * Initialises the server to accept a new client authentication attempt
+ * @param N The safe prime associated with the client's verifier
+ * @param g The group parameter associated with the client's verifier
+ * @param v The client's verifier
+ * @param digest The digest algorithm associated with the client's verifier
+ * @param random For key generation
+ */
+ public virtual void Init(BigInteger N, BigInteger g, BigInteger v, IDigest digest, SecureRandom random)
+ {
+ this.N = N;
+ this.g = g;
+ this.v = v;
+
+ this.random = random;
+ this.digest = digest;
+ }
+
+ /**
+ * Generates the server's credentials that are to be sent to the client.
+ * @return The server's public value to the client
+ */
+ public virtual BigInteger GenerateServerCredentials()
+ {
+ BigInteger k = Srp6Utilities.CalculateK(digest, N, g);
+ this.privB = SelectPrivateValue();
+ this.pubB = k.Multiply(v).Mod(N).Add(g.ModPow(privB, N)).Mod(N);
+
+ return pubB;
+ }
+
+ /**
+ * Processes the client's credentials. If valid the shared secret is generated and returned.
+ * @param clientA The client's credentials
+ * @return A shared secret BigInteger
+ * @throws CryptoException If client's credentials are invalid
+ */
+ public virtual BigInteger CalculateSecret(BigInteger clientA)
+ {
+ this.A = Srp6Utilities.ValidatePublicValue(N, clientA);
+ this.u = Srp6Utilities.CalculateU(digest, N, A, pubB);
+ this.S = CalculateS();
+
+ return S;
+ }
+
+ protected virtual BigInteger SelectPrivateValue()
+ {
+ return Srp6Utilities.GeneratePrivateValue(digest, N, g, random);
+ }
+
+ private BigInteger CalculateS()
+ {
+ return v.ModPow(u, N).Multiply(A).Mod(N).ModPow(privB, N);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/srp/SRP6Utilities.cs b/Crypto/src/crypto/agreement/srp/SRP6Utilities.cs
new file mode 100644
index 000000000..4e790f572
--- /dev/null
+++ b/Crypto/src/crypto/agreement/srp/SRP6Utilities.cs
@@ -0,0 +1,85 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Srp
+{
+ public class Srp6Utilities
+ {
+ public static BigInteger CalculateK(IDigest digest, BigInteger N, BigInteger g)
+ {
+ return HashPaddedPair(digest, N, N, g);
+ }
+
+ public static BigInteger CalculateU(IDigest digest, BigInteger N, BigInteger A, BigInteger B)
+ {
+ return HashPaddedPair(digest, N, A, B);
+ }
+
+ public static BigInteger CalculateX(IDigest digest, BigInteger N, byte[] salt, byte[] identity, byte[] password)
+ {
+ byte[] output = new byte[digest.GetDigestSize()];
+
+ digest.BlockUpdate(identity, 0, identity.Length);
+ digest.Update((byte)':');
+ digest.BlockUpdate(password, 0, password.Length);
+ digest.DoFinal(output, 0);
+
+ digest.BlockUpdate(salt, 0, salt.Length);
+ digest.BlockUpdate(output, 0, output.Length);
+ digest.DoFinal(output, 0);
+
+ return new BigInteger(1, output);
+ }
+
+ public static BigInteger GeneratePrivateValue(IDigest digest, BigInteger N, BigInteger g, SecureRandom random)
+ {
+ int minBits = System.Math.Min(256, N.BitLength / 2);
+ BigInteger min = BigInteger.One.ShiftLeft(minBits - 1);
+ BigInteger max = N.Subtract(BigInteger.One);
+
+ return BigIntegers.CreateRandomInRange(min, max, random);
+ }
+
+ public static BigInteger ValidatePublicValue(BigInteger N, BigInteger val)
+ {
+ val = val.Mod(N);
+
+ // Check that val % N != 0
+ if (val.Equals(BigInteger.Zero))
+ throw new CryptoException("Invalid public value: 0");
+
+ return val;
+ }
+
+ private static BigInteger HashPaddedPair(IDigest digest, BigInteger N, BigInteger n1, BigInteger n2)
+ {
+ int padLength = (N.BitLength + 7) / 8;
+
+ byte[] n1_bytes = GetPadded(n1, padLength);
+ byte[] n2_bytes = GetPadded(n2, padLength);
+
+ digest.BlockUpdate(n1_bytes, 0, n1_bytes.Length);
+ digest.BlockUpdate(n2_bytes, 0, n2_bytes.Length);
+
+ byte[] output = new byte[digest.GetDigestSize()];
+ digest.DoFinal(output, 0);
+
+ return new BigInteger(1, output);
+ }
+
+ private static byte[] GetPadded(BigInteger n, int length)
+ {
+ byte[] bs = BigIntegers.AsUnsignedByteArray(n);
+ if (bs.Length < length)
+ {
+ byte[] tmp = new byte[length];
+ Array.Copy(bs, 0, tmp, length - bs.Length, bs.Length);
+ bs = tmp;
+ }
+ return bs;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs b/Crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
new file mode 100644
index 000000000..264833b4d
--- /dev/null
+++ b/Crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
@@ -0,0 +1,49 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Srp
+{
+ /**
+ * Generates new SRP verifier for user
+ */
+ public class Srp6VerifierGenerator
+ {
+ protected BigInteger N;
+ protected BigInteger g;
+ protected IDigest digest;
+
+ public Srp6VerifierGenerator()
+ {
+ }
+
+ /**
+ * Initialises generator to create new verifiers
+ * @param N The safe prime to use (see DHParametersGenerator)
+ * @param g The group parameter to use (see DHParametersGenerator)
+ * @param digest The digest to use. The same digest type will need to be used later for the actual authentication
+ * attempt. Also note that the final session key size is dependent on the chosen digest.
+ */
+ public virtual void Init(BigInteger N, BigInteger g, IDigest digest)
+ {
+ this.N = N;
+ this.g = g;
+ this.digest = digest;
+ }
+
+ /**
+ * Creates a new SRP verifier
+ * @param salt The salt to use, generally should be large and random
+ * @param identity The user's identifying information (eg. username)
+ * @param password The user's password
+ * @return A new verifier for use in future SRP authentication
+ */
+ public virtual BigInteger GenerateVerifier(byte[] salt, byte[] identity, byte[] password)
+ {
+ BigInteger x = Srp6Utilities.CalculateX(digest, N, salt, identity, password);
+
+ return g.ModPow(x, N);
+ }
+ }
+}
+
diff --git a/Crypto/src/crypto/digests/GOST3411Digest.cs b/Crypto/src/crypto/digests/GOST3411Digest.cs
new file mode 100644
index 000000000..9f0bec9e6
--- /dev/null
+++ b/Crypto/src/crypto/digests/GOST3411Digest.cs
@@ -0,0 +1,343 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * implementation of GOST R 34.11-94
+ */
+ public class Gost3411Digest
+ : IDigest
+ {
+ private const int DIGEST_LENGTH = 32;
+
+ private byte[] H = new byte[32], L = new byte[32],
+ M = new byte[32], Sum = new byte[32];
+ private byte[][] C = MakeC();
+
+ private byte[] xBuf = new byte[32];
+ private int xBufOff;
+ private ulong byteCount;
+
+ private readonly IBlockCipher cipher = new Gost28147Engine();
+ private readonly byte[] sBox;
+
+ private static byte[][] MakeC()
+ {
+ byte[][] c = new byte[4][];
+ for (int i = 0; i < 4; ++i)
+ {
+ c[i] = new byte[32];
+ }
+ return c;
+ }
+
+ /**
+ * Standard constructor
+ */
+ public Gost3411Digest()
+ {
+ sBox = Gost28147Engine.GetSBox("D-A");
+ cipher.Init(true, new ParametersWithSBox(null, sBox));
+
+ Reset();
+ }
+
+ /**
+ * Constructor to allow use of a particular sbox with GOST28147
+ * @see GOST28147Engine#getSBox(String)
+ */
+ public Gost3411Digest(byte[] sBoxParam)
+ {
+ sBox = Arrays.Clone(sBoxParam);
+ cipher.Init(true, new ParametersWithSBox(null, sBox));
+
+ Reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public Gost3411Digest(Gost3411Digest t)
+ {
+ this.sBox = t.sBox;
+ cipher.Init(true, new ParametersWithSBox(null, sBox));
+
+ Reset();
+
+ Array.Copy(t.H, 0, this.H, 0, t.H.Length);
+ Array.Copy(t.L, 0, this.L, 0, t.L.Length);
+ Array.Copy(t.M, 0, this.M, 0, t.M.Length);
+ Array.Copy(t.Sum, 0, this.Sum, 0, t.Sum.Length);
+ Array.Copy(t.C[1], 0, this.C[1], 0, t.C[1].Length);
+ Array.Copy(t.C[2], 0, this.C[2], 0, t.C[2].Length);
+ Array.Copy(t.C[3], 0, this.C[3], 0, t.C[3].Length);
+ Array.Copy(t.xBuf, 0, this.xBuf, 0, t.xBuf.Length);
+
+ this.xBufOff = t.xBufOff;
+ this.byteCount = t.byteCount;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Gost3411"; }
+ }
+
+ public int GetDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ public void Update(
+ byte input)
+ {
+ xBuf[xBufOff++] = input;
+ if (xBufOff == xBuf.Length)
+ {
+ sumByteArray(xBuf); // calc sum M
+ processBlock(xBuf, 0);
+ xBufOff = 0;
+ }
+ byteCount++;
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ while ((xBufOff != 0) && (length > 0))
+ {
+ Update(input[inOff]);
+ inOff++;
+ length--;
+ }
+
+ while (length > xBuf.Length)
+ {
+ Array.Copy(input, inOff, xBuf, 0, xBuf.Length);
+
+ sumByteArray(xBuf); // calc sum M
+ processBlock(xBuf, 0);
+ inOff += xBuf.Length;
+ length -= xBuf.Length;
+ byteCount += (uint)xBuf.Length;
+ }
+
+ // load in the remainder.
+ while (length > 0)
+ {
+ Update(input[inOff]);
+ inOff++;
+ length--;
+ }
+ }
+
+ // (i + 1 + 4(k - 1)) = 8i + k i = 0-3, k = 1-8
+ private byte[] K = new byte[32];
+
+ private byte[] P(byte[] input)
+ {
+ int fourK = 0;
+ for(int k = 0; k < 8; k++)
+ {
+ K[fourK++] = input[k];
+ K[fourK++] = input[8 + k];
+ K[fourK++] = input[16 + k];
+ K[fourK++] = input[24 + k];
+ }
+
+ return K;
+ }
+
+ //A (x) = (x0 ^ x1) || x3 || x2 || x1
+ byte[] a = new byte[8];
+ private byte[] A(byte[] input)
+ {
+ for(int j=0; j<8; j++)
+ {
+ a[j]=(byte)(input[j] ^ input[j+8]);
+ }
+
+ Array.Copy(input, 8, input, 0, 24);
+ Array.Copy(a, 0, input, 24, 8);
+
+ return input;
+ }
+
+ //Encrypt function, ECB mode
+ private void E(byte[] key, byte[] s, int sOff, byte[] input, int inOff)
+ {
+ cipher.Init(true, new KeyParameter(key));
+
+ cipher.ProcessBlock(input, inOff, s, sOff);
+ }
+
+ // (in:) n16||..||n1 ==> (out:) n1^n2^n3^n4^n13^n16||n16||..||n2
+ internal short[] wS = new short[16], w_S = new short[16];
+
+ private void fw(byte[] input)
+ {
+ cpyBytesToShort(input, wS);
+ w_S[15] = (short)(wS[0] ^ wS[1] ^ wS[2] ^ wS[3] ^ wS[12] ^ wS[15]);
+ Array.Copy(wS, 1, w_S, 0, 15);
+ cpyShortToBytes(w_S, input);
+ }
+
+ // block processing
+ internal byte[] S = new byte[32], U = new byte[32], V = new byte[32], W = new byte[32];
+
+ private void processBlock(byte[] input, int inOff)
+ {
+ Array.Copy(input, inOff, M, 0, 32);
+
+ //key step 1
+
+ // H = h3 || h2 || h1 || h0
+ // S = s3 || s2 || s1 || s0
+ H.CopyTo(U, 0);
+ M.CopyTo(V, 0);
+ for (int j=0; j<32; j++)
+ {
+ W[j] = (byte)(U[j]^V[j]);
+ }
+ // Encrypt gost28147-ECB
+ E(P(W), S, 0, H, 0); // s0 = EK0 [h0]
+
+ //keys step 2,3,4
+ for (int i=1; i<4; i++)
+ {
+ byte[] tmpA = A(U);
+ for (int j=0; j<32; j++)
+ {
+ U[j] = (byte)(tmpA[j] ^ C[i][j]);
+ }
+ V = A(A(V));
+ for (int j=0; j<32; j++)
+ {
+ W[j] = (byte)(U[j]^V[j]);
+ }
+ // Encrypt gost28147-ECB
+ E(P(W), S, i * 8, H, i * 8); // si = EKi [hi]
+ }
+
+ // x(M, H) = y61(H^y(M^y12(S)))
+ for(int n = 0; n < 12; n++)
+ {
+ fw(S);
+ }
+ for(int n = 0; n < 32; n++)
+ {
+ S[n] = (byte)(S[n] ^ M[n]);
+ }
+
+ fw(S);
+
+ for(int n = 0; n < 32; n++)
+ {
+ S[n] = (byte)(H[n] ^ S[n]);
+ }
+ for(int n = 0; n < 61; n++)
+ {
+ fw(S);
+ }
+ Array.Copy(S, 0, H, 0, H.Length);
+ }
+
+ private void finish()
+ {
+ ulong bitCount = byteCount * 8;
+ Pack.UInt64_To_LE(bitCount, L);
+
+ while (xBufOff != 0)
+ {
+ Update((byte)0);
+ }
+
+ processBlock(L, 0);
+ processBlock(Sum, 0);
+ }
+
+ public int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ finish();
+
+ H.CopyTo(output, outOff);
+
+ Reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ private static readonly byte[] C2 = {
+ 0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,
+ (byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,
+ 0x00,(byte)0xFF,(byte)0xFF,0x00,(byte)0xFF,0x00,0x00,(byte)0xFF,
+ (byte)0xFF,0x00,0x00,0x00,(byte)0xFF,(byte)0xFF,0x00,(byte)0xFF
+ };
+
+ public void Reset()
+ {
+ byteCount = 0;
+ xBufOff = 0;
+
+ Array.Clear(H, 0, H.Length);
+ Array.Clear(L, 0, L.Length);
+ Array.Clear(M, 0, M.Length);
+ Array.Clear(C[1], 0, C[1].Length); // real index C = +1 because index array with 0.
+ Array.Clear(C[3], 0, C[3].Length);
+ Array.Clear(Sum, 0, Sum.Length);
+ Array.Clear(xBuf, 0, xBuf.Length);
+
+ C2.CopyTo(C[2], 0);
+ }
+
+ // 256 bitsblock modul -> (Sum + a mod (2^256))
+ private void sumByteArray(
+ byte[] input)
+ {
+ int carry = 0;
+
+ for (int i = 0; i != Sum.Length; i++)
+ {
+ int sum = (Sum[i] & 0xff) + (input[i] & 0xff) + carry;
+
+ Sum[i] = (byte)sum;
+
+ carry = sum >> 8;
+ }
+ }
+
+ private static void cpyBytesToShort(byte[] S, short[] wS)
+ {
+ for(int i = 0; i < S.Length / 2; i++)
+ {
+ wS[i] = (short)(((S[i*2+1]<<8)&0xFF00)|(S[i*2]&0xFF));
+ }
+ }
+
+ private static void cpyShortToBytes(short[] wS, byte[] S)
+ {
+ for(int i=0; i<S.Length/2; i++)
+ {
+ S[i*2 + 1] = (byte)(wS[i] >> 8);
+ S[i*2] = (byte)wS[i];
+ }
+ }
+
+ public int GetByteLength()
+ {
+ return 32;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/digests/GeneralDigest.cs b/Crypto/src/crypto/digests/GeneralDigest.cs
new file mode 100644
index 000000000..77c17ed58
--- /dev/null
+++ b/Crypto/src/crypto/digests/GeneralDigest.cs
@@ -0,0 +1,118 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * base implementation of MD4 family style digest as outlined in
+ * "Handbook of Applied Cryptography", pages 344 - 347.
+ */
+ public abstract class GeneralDigest
+ : IDigest
+ {
+ private const int BYTE_LENGTH = 64;
+
+ private byte[] xBuf;
+ private int xBufOff;
+
+ private long byteCount;
+
+ internal GeneralDigest()
+ {
+ xBuf = new byte[4];
+ }
+
+ internal GeneralDigest(GeneralDigest t)
+ {
+ xBuf = new byte[t.xBuf.Length];
+ Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length);
+
+ xBufOff = t.xBufOff;
+ byteCount = t.byteCount;
+ }
+
+ public void Update(byte input)
+ {
+ xBuf[xBufOff++] = input;
+
+ if (xBufOff == xBuf.Length)
+ {
+ ProcessWord(xBuf, 0);
+ xBufOff = 0;
+ }
+
+ byteCount++;
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ //
+ // fill the current word
+ //
+ while ((xBufOff != 0) && (length > 0))
+ {
+ Update(input[inOff]);
+ inOff++;
+ length--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (length > xBuf.Length)
+ {
+ ProcessWord(input, inOff);
+
+ inOff += xBuf.Length;
+ length -= xBuf.Length;
+ byteCount += xBuf.Length;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (length > 0)
+ {
+ Update(input[inOff]);
+
+ inOff++;
+ length--;
+ }
+ }
+
+ public void Finish()
+ {
+ long bitLength = (byteCount << 3);
+
+ //
+ // add the pad bytes.
+ //
+ Update((byte)128);
+
+ while (xBufOff != 0) Update((byte)0);
+ ProcessLength(bitLength);
+ ProcessBlock();
+ }
+
+ public virtual void Reset()
+ {
+ byteCount = 0;
+ xBufOff = 0;
+ Array.Clear(xBuf, 0, xBuf.Length);
+ }
+
+ public int GetByteLength()
+ {
+ return BYTE_LENGTH;
+ }
+
+ internal abstract void ProcessWord(byte[] input, int inOff);
+ internal abstract void ProcessLength(long bitLength);
+ internal abstract void ProcessBlock();
+ public abstract string AlgorithmName { get; }
+ public abstract int GetDigestSize();
+ public abstract int DoFinal(byte[] output, int outOff);
+ }
+}
diff --git a/Crypto/src/crypto/digests/LongDigest.cs b/Crypto/src/crypto/digests/LongDigest.cs
new file mode 100644
index 000000000..702753b2b
--- /dev/null
+++ b/Crypto/src/crypto/digests/LongDigest.cs
@@ -0,0 +1,346 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * Base class for SHA-384 and SHA-512.
+ */
+ public abstract class LongDigest
+ : IDigest
+ {
+ private int MyByteLength = 128;
+
+ private byte[] xBuf;
+ private int xBufOff;
+
+ private long byteCount1;
+ private long byteCount2;
+
+ internal ulong H1, H2, H3, H4, H5, H6, H7, H8;
+
+ private ulong[] W = new ulong[80];
+ private int wOff;
+
+ /**
+ * Constructor for variable length word
+ */
+ internal LongDigest()
+ {
+ xBuf = new byte[8];
+
+ Reset();
+ }
+
+ /**
+ * Copy constructor. We are using copy constructors in place
+ * of the object.Clone() interface as this interface is not
+ * supported by J2ME.
+ */
+ internal LongDigest(
+ LongDigest t)
+ {
+ xBuf = new byte[t.xBuf.Length];
+ Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length);
+
+ xBufOff = t.xBufOff;
+ byteCount1 = t.byteCount1;
+ byteCount2 = t.byteCount2;
+
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+ H8 = t.H8;
+
+ Array.Copy(t.W, 0, W, 0, t.W.Length);
+ wOff = t.wOff;
+ }
+
+ public void Update(
+ byte input)
+ {
+ xBuf[xBufOff++] = input;
+
+ if (xBufOff == xBuf.Length)
+ {
+ ProcessWord(xBuf, 0);
+ xBufOff = 0;
+ }
+
+ byteCount1++;
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ //
+ // fill the current word
+ //
+ while ((xBufOff != 0) && (length > 0))
+ {
+ Update(input[inOff]);
+
+ inOff++;
+ length--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (length > xBuf.Length)
+ {
+ ProcessWord(input, inOff);
+
+ inOff += xBuf.Length;
+ length -= xBuf.Length;
+ byteCount1 += xBuf.Length;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (length > 0)
+ {
+ Update(input[inOff]);
+
+ inOff++;
+ length--;
+ }
+ }
+
+ public void Finish()
+ {
+ AdjustByteCounts();
+
+ long lowBitLength = byteCount1 << 3;
+ long hiBitLength = byteCount2;
+
+ //
+ // add the pad bytes.
+ //
+ Update((byte)128);
+
+ while (xBufOff != 0)
+ {
+ Update((byte)0);
+ }
+
+ ProcessLength(lowBitLength, hiBitLength);
+
+ ProcessBlock();
+ }
+
+ public virtual void Reset()
+ {
+ byteCount1 = 0;
+ byteCount2 = 0;
+
+ xBufOff = 0;
+ for ( int i = 0; i < xBuf.Length; i++ )
+ {
+ xBuf[i] = 0;
+ }
+
+ wOff = 0;
+ Array.Clear(W, 0, W.Length);
+ }
+
+ internal void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ W[wOff] = Pack.BE_To_UInt64(input, inOff);
+
+ if (++wOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ /**
+ * adjust the byte counts so that byteCount2 represents the
+ * upper long (less 3 bits) word of the byte count.
+ */
+ private void AdjustByteCounts()
+ {
+ if (byteCount1 > 0x1fffffffffffffffL)
+ {
+ byteCount2 += (long) ((ulong) byteCount1 >> 61);
+ byteCount1 &= 0x1fffffffffffffffL;
+ }
+ }
+
+ internal void ProcessLength(
+ long lowW,
+ long hiW)
+ {
+ if (wOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ W[14] = (ulong)hiW;
+ W[15] = (ulong)lowW;
+ }
+
+ internal void ProcessBlock()
+ {
+ AdjustByteCounts();
+
+ //
+ // expand 16 word block into 80 word blocks.
+ //
+ for (int ti = 16; ti <= 79; ++ti)
+ {
+ W[ti] = Sigma1(W[ti - 2]) + W[ti - 7] + Sigma0(W[ti - 15]) + W[ti - 16];
+ }
+
+ //
+ // set up working variables.
+ //
+ ulong a = H1;
+ ulong b = H2;
+ ulong c = H3;
+ ulong d = H4;
+ ulong e = H5;
+ ulong f = H6;
+ ulong g = H7;
+ ulong h = H8;
+
+ int t = 0;
+ for(int i = 0; i < 10; i ++)
+ {
+ // t = 8 * i
+ h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++];
+ d += h;
+ h += Sum0(a) + Maj(a, b, c);
+
+ // t = 8 * i + 1
+ g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++];
+ c += g;
+ g += Sum0(h) + Maj(h, a, b);
+
+ // t = 8 * i + 2
+ f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++];
+ b += f;
+ f += Sum0(g) + Maj(g, h, a);
+
+ // t = 8 * i + 3
+ e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++];
+ a += e;
+ e += Sum0(f) + Maj(f, g, h);
+
+ // t = 8 * i + 4
+ d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++];
+ h += d;
+ d += Sum0(e) + Maj(e, f, g);
+
+ // t = 8 * i + 5
+ c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++];
+ g += c;
+ c += Sum0(d) + Maj(d, e, f);
+
+ // t = 8 * i + 6
+ b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++];
+ f += b;
+ b += Sum0(c) + Maj(c, d, e);
+
+ // t = 8 * i + 7
+ a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++];
+ e += a;
+ a += Sum0(b) + Maj(b, c, d);
+ }
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+ H5 += e;
+ H6 += f;
+ H7 += g;
+ H8 += h;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ wOff = 0;
+ Array.Clear(W, 0, 16);
+ }
+
+ /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */
+ private static ulong Ch(ulong x, ulong y, ulong z)
+ {
+ return (x & y) ^ (~x & z);
+ }
+
+ private static ulong Maj(ulong x, ulong y, ulong z)
+ {
+ return (x & y) ^ (x & z) ^ (y & z);
+ }
+
+ private static ulong Sum0(ulong x)
+ {
+ return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39));
+ }
+
+ private static ulong Sum1(ulong x)
+ {
+ return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41));
+ }
+
+ private static ulong Sigma0(ulong x)
+ {
+ return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7);
+ }
+
+ private static ulong Sigma1(ulong x)
+ {
+ return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6);
+ }
+
+ /* SHA-384 and SHA-512 Constants
+ * (represent the first 64 bits of the fractional parts of the
+ * cube roots of the first sixty-four prime numbers)
+ */
+ internal static readonly ulong[] K =
+ {
+ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
+ 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
+ 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
+ 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
+ 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
+ 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
+ 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
+ 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
+ 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+ 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
+ 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
+ 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
+ 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
+ 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+ 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
+ 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
+ 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
+ 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
+ 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
+ };
+
+ public int GetByteLength()
+ {
+ return MyByteLength;
+ }
+
+ public abstract string AlgorithmName { get; }
+ public abstract int GetDigestSize();
+ public abstract int DoFinal(byte[] output, int outOff);
+ }
+}
diff --git a/Crypto/src/crypto/digests/MD2Digest.cs b/Crypto/src/crypto/digests/MD2Digest.cs
new file mode 100644
index 000000000..78c696f33
--- /dev/null
+++ b/Crypto/src/crypto/digests/MD2Digest.cs
@@ -0,0 +1,247 @@
+using System;
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+
+ /**
+ * implementation of MD2
+ * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992
+ */
+ public class MD2Digest
+ : IDigest
+ {
+ private const int DigestLength = 16;
+ private const int BYTE_LENGTH = 16;
+
+ /* X buffer */
+ private byte[] X = new byte[48];
+ private int xOff;
+
+ /* M buffer */
+
+ private byte[] M = new byte[16];
+ private int mOff;
+
+ /* check sum */
+
+ private byte[] C = new byte[16];
+ private int COff;
+
+ public MD2Digest()
+ {
+ Reset();
+ }
+ public MD2Digest(MD2Digest t)
+ {
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ Array.Copy(t.M, 0, M, 0, t.M.Length);
+ mOff = t.mOff;
+ Array.Copy(t.C, 0, C, 0, t.C.Length);
+ COff = t.COff;
+ }
+ /**
+ * return the algorithm name
+ *
+ * @return the algorithm name
+ */
+ public string AlgorithmName
+ {
+ get { return "MD2"; }
+ }
+
+ public int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ public int GetByteLength()
+ {
+ return BYTE_LENGTH;
+ }
+
+ /**
+ * Close the digest, producing the final digest value. The doFinal
+ * call leaves the digest reset.
+ *
+ * @param out the array the digest is to be copied into.
+ * @param outOff the offset into the out array the digest is to start at.
+ */
+ public int DoFinal(byte[] output, int outOff)
+ {
+ // add padding
+ byte paddingByte = (byte)(M.Length - mOff);
+ for (int i=mOff;i<M.Length;i++)
+ {
+ M[i] = paddingByte;
+ }
+ //do final check sum
+ ProcessChecksum(M);
+ // do final block process
+ ProcessBlock(M);
+
+ ProcessBlock(C);
+
+ Array.Copy(X, xOff, output, outOff, 16);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the digest back to it's initial state.
+ */
+ public void Reset()
+ {
+ xOff = 0;
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ mOff = 0;
+ for (int i = 0; i != M.Length; i++)
+ {
+ M[i] = 0;
+ }
+ COff = 0;
+ for (int i = 0; i != C.Length; i++)
+ {
+ C[i] = 0;
+ }
+ }
+ /**
+ * update the message digest with a single byte.
+ *
+ * @param in the input byte to be entered.
+ */
+ public void Update(byte input)
+ {
+ M[mOff++] = input;
+
+ if (mOff == 16)
+ {
+ ProcessChecksum(M);
+ ProcessBlock(M);
+ mOff = 0;
+ }
+ }
+
+ /**
+ * update the message digest with a block of bytes.
+ *
+ * @param in the byte array containing the data.
+ * @param inOff the offset into the byte array where the data starts.
+ * @param len the length of the data.
+ */
+ public void BlockUpdate(byte[] input, int inOff, int length)
+ {
+ //
+ // fill the current word
+ //
+ while ((mOff != 0) && (length > 0))
+ {
+ Update(input[inOff]);
+ inOff++;
+ length--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (length > 16)
+ {
+ Array.Copy(input,inOff,M,0,16);
+ ProcessChecksum(M);
+ ProcessBlock(M);
+ length -= 16;
+ inOff += 16;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (length > 0)
+ {
+ Update(input[inOff]);
+ inOff++;
+ length--;
+ }
+ }
+
+ internal void ProcessChecksum(byte[] m)
+ {
+ int L = C[15];
+ for (int i=0;i<16;i++)
+ {
+ C[i] ^= S[(m[i] ^ L) & 0xff];
+ L = C[i];
+ }
+ }
+ internal void ProcessBlock(byte[] m)
+ {
+ for (int i=0;i<16;i++)
+ {
+ X[i+16] = m[i];
+ X[i+32] = (byte)(m[i] ^ X[i]);
+ }
+ // encrypt block
+ int t = 0;
+
+ for (int j=0;j<18;j++)
+ {
+ for (int k=0;k<48;k++)
+ {
+ t = X[k] ^= S[t];
+ t = t & 0xff;
+ }
+ t = (t + j)%256;
+ }
+ }
+
+
+
+ // 256-byte random permutation constructed from the digits of PI
+ private static readonly byte[] S = {
+ (byte)41,(byte)46,(byte)67,(byte)201,(byte)162,(byte)216,(byte)124,
+ (byte)1,(byte)61,(byte)54,(byte)84,(byte)161,(byte)236,(byte)240,
+ (byte)6,(byte)19,(byte)98,(byte)167,(byte)5,(byte)243,(byte)192,
+ (byte)199,(byte)115,(byte)140,(byte)152,(byte)147,(byte)43,(byte)217,
+ (byte)188,(byte)76,(byte)130,(byte)202,(byte)30,(byte)155,(byte)87,
+ (byte)60,(byte)253,(byte)212,(byte)224,(byte)22,(byte)103,(byte)66,
+ (byte)111,(byte)24,(byte)138,(byte)23,(byte)229,(byte)18,(byte)190,
+ (byte)78,(byte)196,(byte)214,(byte)218,(byte)158,(byte)222,(byte)73,
+ (byte)160,(byte)251,(byte)245,(byte)142,(byte)187,(byte)47,(byte)238,
+ (byte)122,(byte)169,(byte)104,(byte)121,(byte)145,(byte)21,(byte)178,
+ (byte)7,(byte)63,(byte)148,(byte)194,(byte)16,(byte)137,(byte)11,
+ (byte)34,(byte)95,(byte)33,(byte)128,(byte)127,(byte)93,(byte)154,
+ (byte)90,(byte)144,(byte)50,(byte)39,(byte)53,(byte)62,(byte)204,
+ (byte)231,(byte)191,(byte)247,(byte)151,(byte)3,(byte)255,(byte)25,
+ (byte)48,(byte)179,(byte)72,(byte)165,(byte)181,(byte)209,(byte)215,
+ (byte)94,(byte)146,(byte)42,(byte)172,(byte)86,(byte)170,(byte)198,
+ (byte)79,(byte)184,(byte)56,(byte)210,(byte)150,(byte)164,(byte)125,
+ (byte)182,(byte)118,(byte)252,(byte)107,(byte)226,(byte)156,(byte)116,
+ (byte)4,(byte)241,(byte)69,(byte)157,(byte)112,(byte)89,(byte)100,
+ (byte)113,(byte)135,(byte)32,(byte)134,(byte)91,(byte)207,(byte)101,
+ (byte)230,(byte)45,(byte)168,(byte)2,(byte)27,(byte)96,(byte)37,
+ (byte)173,(byte)174,(byte)176,(byte)185,(byte)246,(byte)28,(byte)70,
+ (byte)97,(byte)105,(byte)52,(byte)64,(byte)126,(byte)15,(byte)85,
+ (byte)71,(byte)163,(byte)35,(byte)221,(byte)81,(byte)175,(byte)58,
+ (byte)195,(byte)92,(byte)249,(byte)206,(byte)186,(byte)197,(byte)234,
+ (byte)38,(byte)44,(byte)83,(byte)13,(byte)110,(byte)133,(byte)40,
+ (byte)132, 9,(byte)211,(byte)223,(byte)205,(byte)244,(byte)65,
+ (byte)129,(byte)77,(byte)82,(byte)106,(byte)220,(byte)55,(byte)200,
+ (byte)108,(byte)193,(byte)171,(byte)250,(byte)36,(byte)225,(byte)123,
+ (byte)8,(byte)12,(byte)189,(byte)177,(byte)74,(byte)120,(byte)136,
+ (byte)149,(byte)139,(byte)227,(byte)99,(byte)232,(byte)109,(byte)233,
+ (byte)203,(byte)213,(byte)254,(byte)59,(byte)0,(byte)29,(byte)57,
+ (byte)242,(byte)239,(byte)183,(byte)14,(byte)102,(byte)88,(byte)208,
+ (byte)228,(byte)166,(byte)119,(byte)114,(byte)248,(byte)235,(byte)117,
+ (byte)75,(byte)10,(byte)49,(byte)68,(byte)80,(byte)180,(byte)143,
+ (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51,
+ (byte)159,(byte)17,(byte)131,(byte)20
+ };
+ }
+
+}
diff --git a/Crypto/src/crypto/digests/MD4Digest.cs b/Crypto/src/crypto/digests/MD4Digest.cs
new file mode 100644
index 000000000..bc4eae0fd
--- /dev/null
+++ b/Crypto/src/crypto/digests/MD4Digest.cs
@@ -0,0 +1,271 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * implementation of MD4 as RFC 1320 by R. Rivest, MIT Laboratory for
+ * Computer Science and RSA Data Security, Inc.
+ * <p>
+ * <b>NOTE</b>: This algorithm is only included for backwards compatibility
+ * with legacy applications, it's not secure, don't use it for anything new!</p>
+ */
+ public class MD4Digest
+ : GeneralDigest
+ {
+ private const int DigestLength = 16;
+
+ private int H1, H2, H3, H4; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public MD4Digest()
+ {
+ Reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public MD4Digest(MD4Digest t) : base(t)
+ {
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "MD4"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ internal override void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8)
+ | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ internal override void ProcessLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)((ulong) bitLength >> 32);
+ }
+
+ private void UnpackWord(
+ int word,
+ byte[] outBytes,
+ int outOff)
+ {
+ outBytes[outOff] = (byte)word;
+ outBytes[outOff + 1] = (byte)((uint) word >> 8);
+ outBytes[outOff + 2] = (byte)((uint) word >> 16);
+ outBytes[outOff + 3] = (byte)((uint) word >> 24);
+ }
+
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ UnpackWord(H1, output, outOff);
+ UnpackWord(H2, output, outOff + 4);
+ UnpackWord(H3, output, outOff + 8);
+ UnpackWord(H4, output, outOff + 12);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public override void Reset()
+ {
+ base.Reset();
+
+ H1 = unchecked((int) 0x67452301);
+ H2 = unchecked((int) 0xefcdab89);
+ H3 = unchecked((int) 0x98badcfe);
+ H4 = unchecked((int) 0x10325476);
+
+ xOff = 0;
+
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ //
+ // round 1 left rotates
+ //
+ private const int S11 = 3;
+ private const int S12 = 7;
+ private const int S13 = 11;
+ private const int S14 = 19;
+
+ //
+ // round 2 left rotates
+ //
+ private const int S21 = 3;
+ private const int S22 = 5;
+ private const int S23 = 9;
+ private const int S24 = 13;
+
+ //
+ // round 3 left rotates
+ //
+ private const int S31 = 3;
+ private const int S32 = 9;
+ private const int S33 = 11;
+ private const int S34 = 15;
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RotateLeft(
+ int x,
+ int n)
+ {
+ return (x << n) | (int) ((uint) x >> (32 - n));
+ }
+
+ /*
+ * F, G, H and I are the basic MD4 functions.
+ */
+ private int F(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & v) | (~u & w);
+ }
+
+ private int G(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & v) | (u & w) | (v & w);
+ }
+
+ private int H(
+ int u,
+ int v,
+ int w)
+ {
+ return u ^ v ^ w;
+ }
+
+ internal override void ProcessBlock()
+ {
+ int a = H1;
+ int b = H2;
+ int c = H3;
+ int d = H4;
+
+ //
+ // Round 1 - F cycle, 16 times.
+ //
+ a = RotateLeft((a + F(b, c, d) + X[ 0]), S11);
+ d = RotateLeft((d + F(a, b, c) + X[ 1]), S12);
+ c = RotateLeft((c + F(d, a, b) + X[ 2]), S13);
+ b = RotateLeft((b + F(c, d, a) + X[ 3]), S14);
+ a = RotateLeft((a + F(b, c, d) + X[ 4]), S11);
+ d = RotateLeft((d + F(a, b, c) + X[ 5]), S12);
+ c = RotateLeft((c + F(d, a, b) + X[ 6]), S13);
+ b = RotateLeft((b + F(c, d, a) + X[ 7]), S14);
+ a = RotateLeft((a + F(b, c, d) + X[ 8]), S11);
+ d = RotateLeft((d + F(a, b, c) + X[ 9]), S12);
+ c = RotateLeft((c + F(d, a, b) + X[10]), S13);
+ b = RotateLeft((b + F(c, d, a) + X[11]), S14);
+ a = RotateLeft((a + F(b, c, d) + X[12]), S11);
+ d = RotateLeft((d + F(a, b, c) + X[13]), S12);
+ c = RotateLeft((c + F(d, a, b) + X[14]), S13);
+ b = RotateLeft((b + F(c, d, a) + X[15]), S14);
+
+ //
+ // Round 2 - G cycle, 16 times.
+ //
+ a = RotateLeft((a + G(b, c, d) + X[ 0] + 0x5a827999), S21);
+ d = RotateLeft((d + G(a, b, c) + X[ 4] + 0x5a827999), S22);
+ c = RotateLeft((c + G(d, a, b) + X[ 8] + 0x5a827999), S23);
+ b = RotateLeft((b + G(c, d, a) + X[12] + 0x5a827999), S24);
+ a = RotateLeft((a + G(b, c, d) + X[ 1] + 0x5a827999), S21);
+ d = RotateLeft((d + G(a, b, c) + X[ 5] + 0x5a827999), S22);
+ c = RotateLeft((c + G(d, a, b) + X[ 9] + 0x5a827999), S23);
+ b = RotateLeft((b + G(c, d, a) + X[13] + 0x5a827999), S24);
+ a = RotateLeft((a + G(b, c, d) + X[ 2] + 0x5a827999), S21);
+ d = RotateLeft((d + G(a, b, c) + X[ 6] + 0x5a827999), S22);
+ c = RotateLeft((c + G(d, a, b) + X[10] + 0x5a827999), S23);
+ b = RotateLeft((b + G(c, d, a) + X[14] + 0x5a827999), S24);
+ a = RotateLeft((a + G(b, c, d) + X[ 3] + 0x5a827999), S21);
+ d = RotateLeft((d + G(a, b, c) + X[ 7] + 0x5a827999), S22);
+ c = RotateLeft((c + G(d, a, b) + X[11] + 0x5a827999), S23);
+ b = RotateLeft((b + G(c, d, a) + X[15] + 0x5a827999), S24);
+
+ //
+ // Round 3 - H cycle, 16 times.
+ //
+ a = RotateLeft((a + H(b, c, d) + X[ 0] + 0x6ed9eba1), S31);
+ d = RotateLeft((d + H(a, b, c) + X[ 8] + 0x6ed9eba1), S32);
+ c = RotateLeft((c + H(d, a, b) + X[ 4] + 0x6ed9eba1), S33);
+ b = RotateLeft((b + H(c, d, a) + X[12] + 0x6ed9eba1), S34);
+ a = RotateLeft((a + H(b, c, d) + X[ 2] + 0x6ed9eba1), S31);
+ d = RotateLeft((d + H(a, b, c) + X[10] + 0x6ed9eba1), S32);
+ c = RotateLeft((c + H(d, a, b) + X[ 6] + 0x6ed9eba1), S33);
+ b = RotateLeft((b + H(c, d, a) + X[14] + 0x6ed9eba1), S34);
+ a = RotateLeft((a + H(b, c, d) + X[ 1] + 0x6ed9eba1), S31);
+ d = RotateLeft((d + H(a, b, c) + X[ 9] + 0x6ed9eba1), S32);
+ c = RotateLeft((c + H(d, a, b) + X[ 5] + 0x6ed9eba1), S33);
+ b = RotateLeft((b + H(c, d, a) + X[13] + 0x6ed9eba1), S34);
+ a = RotateLeft((a + H(b, c, d) + X[ 3] + 0x6ed9eba1), S31);
+ d = RotateLeft((d + H(a, b, c) + X[11] + 0x6ed9eba1), S32);
+ c = RotateLeft((c + H(d, a, b) + X[ 7] + 0x6ed9eba1), S33);
+ b = RotateLeft((b + H(c, d, a) + X[15] + 0x6ed9eba1), S34);
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/digests/MD5Digest.cs b/Crypto/src/crypto/digests/MD5Digest.cs
new file mode 100644
index 000000000..50d93e4f8
--- /dev/null
+++ b/Crypto/src/crypto/digests/MD5Digest.cs
@@ -0,0 +1,301 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347.
+ */
+ public class MD5Digest
+ : GeneralDigest
+ {
+ private const int DigestLength = 16;
+
+ private int H1, H2, H3, H4; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ public MD5Digest()
+ {
+ Reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public MD5Digest(MD5Digest t)
+ : base(t)
+ {
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "MD5"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ internal override void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8)
+ | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ internal override void ProcessLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)((ulong) bitLength >> 32);
+ }
+
+ private void UnpackWord(
+ int word,
+ byte[] outBytes,
+ int outOff)
+ {
+ outBytes[outOff] = (byte)word;
+ outBytes[outOff + 1] = (byte)((uint) word >> 8);
+ outBytes[outOff + 2] = (byte)((uint) word >> 16);
+ outBytes[outOff + 3] = (byte)((uint) word >> 24);
+ }
+
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ UnpackWord(H1, output, outOff);
+ UnpackWord(H2, output, outOff + 4);
+ UnpackWord(H3, output, outOff + 8);
+ UnpackWord(H4, output, outOff + 12);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public override void Reset()
+ {
+ base.Reset();
+
+ H1 = unchecked((int) 0x67452301);
+ H2 = unchecked((int) 0xefcdab89);
+ H3 = unchecked((int) 0x98badcfe);
+ H4 = unchecked((int) 0x10325476);
+
+ xOff = 0;
+
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ //
+ // round 1 left rotates
+ //
+ private static readonly int S11 = 7;
+ private static readonly int S12 = 12;
+ private static readonly int S13 = 17;
+ private static readonly int S14 = 22;
+
+ //
+ // round 2 left rotates
+ //
+ private static readonly int S21 = 5;
+ private static readonly int S22 = 9;
+ private static readonly int S23 = 14;
+ private static readonly int S24 = 20;
+
+ //
+ // round 3 left rotates
+ //
+ private static readonly int S31 = 4;
+ private static readonly int S32 = 11;
+ private static readonly int S33 = 16;
+ private static readonly int S34 = 23;
+
+ //
+ // round 4 left rotates
+ //
+ private static readonly int S41 = 6;
+ private static readonly int S42 = 10;
+ private static readonly int S43 = 15;
+ private static readonly int S44 = 21;
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RotateLeft(
+ int x,
+ int n)
+ {
+ return (x << n) | (int) ((uint) x >> (32 - n));
+ }
+
+ /*
+ * F, G, H and I are the basic MD5 functions.
+ */
+ private int F(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & v) | (~u & w);
+ }
+
+ private int G(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & w) | (v & ~w);
+ }
+
+ private int H(
+ int u,
+ int v,
+ int w)
+ {
+ return u ^ v ^ w;
+ }
+
+ private int K(
+ int u,
+ int v,
+ int w)
+ {
+ return v ^ (u | ~w);
+ }
+
+ internal override void ProcessBlock()
+ {
+ int a = H1;
+ int b = H2;
+ int c = H3;
+ int d = H4;
+
+ //
+ // Round 1 - F cycle, 16 times.
+ //
+ a = RotateLeft((a + F(b, c, d) + X[ 0] + unchecked((int) 0xd76aa478)), S11) + b;
+ d = RotateLeft((d + F(a, b, c) + X[ 1] + unchecked((int) 0xe8c7b756)), S12) + a;
+ c = RotateLeft((c + F(d, a, b) + X[ 2] + unchecked((int) 0x242070db)), S13) + d;
+ b = RotateLeft((b + F(c, d, a) + X[ 3] + unchecked((int) 0xc1bdceee)), S14) + c;
+ a = RotateLeft((a + F(b, c, d) + X[ 4] + unchecked((int) 0xf57c0faf)), S11) + b;
+ d = RotateLeft((d + F(a, b, c) + X[ 5] + unchecked((int) 0x4787c62a)), S12) + a;
+ c = RotateLeft((c + F(d, a, b) + X[ 6] + unchecked((int) 0xa8304613)), S13) + d;
+ b = RotateLeft((b + F(c, d, a) + X[ 7] + unchecked((int) 0xfd469501)), S14) + c;
+ a = RotateLeft((a + F(b, c, d) + X[ 8] + unchecked((int) 0x698098d8)), S11) + b;
+ d = RotateLeft((d + F(a, b, c) + X[ 9] + unchecked((int) 0x8b44f7af)), S12) + a;
+ c = RotateLeft((c + F(d, a, b) + X[10] + unchecked((int) 0xffff5bb1)), S13) + d;
+ b = RotateLeft((b + F(c, d, a) + X[11] + unchecked((int) 0x895cd7be)), S14) + c;
+ a = RotateLeft((a + F(b, c, d) + X[12] + unchecked((int) 0x6b901122)), S11) + b;
+ d = RotateLeft((d + F(a, b, c) + X[13] + unchecked((int) 0xfd987193)), S12) + a;
+ c = RotateLeft((c + F(d, a, b) + X[14] + unchecked((int) 0xa679438e)), S13) + d;
+ b = RotateLeft((b + F(c, d, a) + X[15] + unchecked((int) 0x49b40821)), S14) + c;
+
+ //
+ // Round 2 - G cycle, 16 times.
+ //
+ a = RotateLeft((a + G(b, c, d) + X[ 1] + unchecked((int) 0xf61e2562)), S21) + b;
+ d = RotateLeft((d + G(a, b, c) + X[ 6] + unchecked((int) 0xc040b340)), S22) + a;
+ c = RotateLeft((c + G(d, a, b) + X[11] + unchecked((int) 0x265e5a51)), S23) + d;
+ b = RotateLeft((b + G(c, d, a) + X[ 0] + unchecked((int) 0xe9b6c7aa)), S24) + c;
+ a = RotateLeft((a + G(b, c, d) + X[ 5] + unchecked((int) 0xd62f105d)), S21) + b;
+ d = RotateLeft((d + G(a, b, c) + X[10] + unchecked((int) 0x02441453)), S22) + a;
+ c = RotateLeft((c + G(d, a, b) + X[15] + unchecked((int) 0xd8a1e681)), S23) + d;
+ b = RotateLeft((b + G(c, d, a) + X[ 4] + unchecked((int) 0xe7d3fbc8)), S24) + c;
+ a = RotateLeft((a + G(b, c, d) + X[ 9] + unchecked((int) 0x21e1cde6)), S21) + b;
+ d = RotateLeft((d + G(a, b, c) + X[14] + unchecked((int) 0xc33707d6)), S22) + a;
+ c = RotateLeft((c + G(d, a, b) + X[ 3] + unchecked((int) 0xf4d50d87)), S23) + d;
+ b = RotateLeft((b + G(c, d, a) + X[ 8] + unchecked((int) 0x455a14ed)), S24) + c;
+ a = RotateLeft((a + G(b, c, d) + X[13] + unchecked((int) 0xa9e3e905)), S21) + b;
+ d = RotateLeft((d + G(a, b, c) + X[ 2] + unchecked((int) 0xfcefa3f8)), S22) + a;
+ c = RotateLeft((c + G(d, a, b) + X[ 7] + unchecked((int) 0x676f02d9)), S23) + d;
+ b = RotateLeft((b + G(c, d, a) + X[12] + unchecked((int) 0x8d2a4c8a)), S24) + c;
+
+ //
+ // Round 3 - H cycle, 16 times.
+ //
+ a = RotateLeft((a + H(b, c, d) + X[ 5] + unchecked((int) 0xfffa3942)), S31) + b;
+ d = RotateLeft((d + H(a, b, c) + X[ 8] + unchecked((int) 0x8771f681)), S32) + a;
+ c = RotateLeft((c + H(d, a, b) + X[11] + unchecked((int) 0x6d9d6122)), S33) + d;
+ b = RotateLeft((b + H(c, d, a) + X[14] + unchecked((int) 0xfde5380c)), S34) + c;
+ a = RotateLeft((a + H(b, c, d) + X[ 1] + unchecked((int) 0xa4beea44)), S31) + b;
+ d = RotateLeft((d + H(a, b, c) + X[ 4] + unchecked((int) 0x4bdecfa9)), S32) + a;
+ c = RotateLeft((c + H(d, a, b) + X[ 7] + unchecked((int) 0xf6bb4b60)), S33) + d;
+ b = RotateLeft((b + H(c, d, a) + X[10] + unchecked((int) 0xbebfbc70)), S34) + c;
+ a = RotateLeft((a + H(b, c, d) + X[13] + unchecked((int) 0x289b7ec6)), S31) + b;
+ d = RotateLeft((d + H(a, b, c) + X[ 0] + unchecked((int) 0xeaa127fa)), S32) + a;
+ c = RotateLeft((c + H(d, a, b) + X[ 3] + unchecked((int) 0xd4ef3085)), S33) + d;
+ b = RotateLeft((b + H(c, d, a) + X[ 6] + unchecked((int) 0x04881d05)), S34) + c;
+ a = RotateLeft((a + H(b, c, d) + X[ 9] + unchecked((int) 0xd9d4d039)), S31) + b;
+ d = RotateLeft((d + H(a, b, c) + X[12] + unchecked((int) 0xe6db99e5)), S32) + a;
+ c = RotateLeft((c + H(d, a, b) + X[15] + unchecked((int) 0x1fa27cf8)), S33) + d;
+ b = RotateLeft((b + H(c, d, a) + X[ 2] + unchecked((int) 0xc4ac5665)), S34) + c;
+
+ //
+ // Round 4 - K cycle, 16 times.
+ //
+ a = RotateLeft((a + K(b, c, d) + X[ 0] + unchecked((int) 0xf4292244)), S41) + b;
+ d = RotateLeft((d + K(a, b, c) + X[ 7] + unchecked((int) 0x432aff97)), S42) + a;
+ c = RotateLeft((c + K(d, a, b) + X[14] + unchecked((int) 0xab9423a7)), S43) + d;
+ b = RotateLeft((b + K(c, d, a) + X[ 5] + unchecked((int) 0xfc93a039)), S44) + c;
+ a = RotateLeft((a + K(b, c, d) + X[12] + unchecked((int) 0x655b59c3)), S41) + b;
+ d = RotateLeft((d + K(a, b, c) + X[ 3] + unchecked((int) 0x8f0ccc92)), S42) + a;
+ c = RotateLeft((c + K(d, a, b) + X[10] + unchecked((int) 0xffeff47d)), S43) + d;
+ b = RotateLeft((b + K(c, d, a) + X[ 1] + unchecked((int) 0x85845dd1)), S44) + c;
+ a = RotateLeft((a + K(b, c, d) + X[ 8] + unchecked((int) 0x6fa87e4f)), S41) + b;
+ d = RotateLeft((d + K(a, b, c) + X[15] + unchecked((int) 0xfe2ce6e0)), S42) + a;
+ c = RotateLeft((c + K(d, a, b) + X[ 6] + unchecked((int) 0xa3014314)), S43) + d;
+ b = RotateLeft((b + K(c, d, a) + X[13] + unchecked((int) 0x4e0811a1)), S44) + c;
+ a = RotateLeft((a + K(b, c, d) + X[ 4] + unchecked((int) 0xf7537e82)), S41) + b;
+ d = RotateLeft((d + K(a, b, c) + X[11] + unchecked((int) 0xbd3af235)), S42) + a;
+ c = RotateLeft((c + K(d, a, b) + X[ 2] + unchecked((int) 0x2ad7d2bb)), S43) + d;
+ b = RotateLeft((b + K(c, d, a) + X[ 9] + unchecked((int) 0xeb86d391)), S44) + c;
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/digests/NullDigest.cs b/Crypto/src/crypto/digests/NullDigest.cs
new file mode 100644
index 000000000..e598cb145
--- /dev/null
+++ b/Crypto/src/crypto/digests/NullDigest.cs
@@ -0,0 +1,49 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ public class NullDigest : IDigest
+ {
+ private readonly MemoryStream bOut = new MemoryStream();
+
+ public string AlgorithmName
+ {
+ get { return "NULL"; }
+ }
+
+ public int GetByteLength()
+ {
+ // TODO Is this okay?
+ return 0;
+ }
+
+ public int GetDigestSize()
+ {
+ return (int) bOut.Length;
+ }
+
+ public void Update(byte b)
+ {
+ bOut.WriteByte(b);
+ }
+
+ public void BlockUpdate(byte[] inBytes, int inOff, int len)
+ {
+ bOut.Write(inBytes, inOff, len);
+ }
+
+ public int DoFinal(byte[] outBytes, int outOff)
+ {
+ byte[] res = bOut.ToArray();
+ res.CopyTo(outBytes, outOff);
+ Reset();
+ return res.Length;
+ }
+
+ public void Reset()
+ {
+ bOut.SetLength(0);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/digests/RipeMD128Digest.cs b/Crypto/src/crypto/digests/RipeMD128Digest.cs
new file mode 100644
index 000000000..8977583a4
--- /dev/null
+++ b/Crypto/src/crypto/digests/RipeMD128Digest.cs
@@ -0,0 +1,462 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * implementation of RipeMD128
+ */
+ public class RipeMD128Digest
+ : GeneralDigest
+ {
+ private const int DigestLength = 16;
+
+ private int H0, H1, H2, H3; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public RipeMD128Digest()
+ {
+ Reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public RipeMD128Digest(RipeMD128Digest t) : base(t)
+ {
+ H0 = t.H0;
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "RIPEMD128"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ internal override void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8)
+ | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ internal override void ProcessLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)((ulong) bitLength >> 32);
+ }
+
+ private void UnpackWord(
+ int word,
+ byte[] outBytes,
+ int outOff)
+ {
+ outBytes[outOff] = (byte)word;
+ outBytes[outOff + 1] = (byte)((uint) word >> 8);
+ outBytes[outOff + 2] = (byte)((uint) word >> 16);
+ outBytes[outOff + 3] = (byte)((uint) word >> 24);
+ }
+
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ UnpackWord(H0, output, outOff);
+ UnpackWord(H1, output, outOff + 4);
+ UnpackWord(H2, output, outOff + 8);
+ UnpackWord(H3, output, outOff + 12);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public override void Reset()
+ {
+ base.Reset();
+
+ H0 = unchecked((int) 0x67452301);
+ H1 = unchecked((int) 0xefcdab89);
+ H2 = unchecked((int) 0x98badcfe);
+ H3 = unchecked((int) 0x10325476);
+
+ xOff = 0;
+
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RL(
+ int x,
+ int n)
+ {
+ return (x << n) | (int) ((uint) x >> (32 - n));
+ }
+
+ /*
+ * f1,f2,f3,f4 are the basic RipeMD128 functions.
+ */
+
+ /*
+ * F
+ */
+ private int F1(
+ int x,
+ int y,
+ int z)
+ {
+ return x ^ y ^ z;
+ }
+
+ /*
+ * G
+ */
+ private int F2(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & y) | (~x & z);
+ }
+
+ /*
+ * H
+ */
+ private int F3(
+ int x,
+ int y,
+ int z)
+ {
+ return (x | ~y) ^ z;
+ }
+
+ /*
+ * I
+ */
+ private int F4(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & z) | (y & ~z);
+ }
+
+ private int F1(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + F1(b, c, d) + x, s);
+ }
+
+ private int F2(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + F2(b, c, d) + x + unchecked((int) 0x5a827999), s);
+ }
+
+ private int F3(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + F3(b, c, d) + x + unchecked((int) 0x6ed9eba1), s);
+ }
+
+ private int F4(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + F4(b, c, d) + x + unchecked((int) 0x8f1bbcdc), s);
+ }
+
+ private int FF1(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + F1(b, c, d) + x, s);
+ }
+
+ private int FF2(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + F2(b, c, d) + x + unchecked((int) 0x6d703ef3), s);
+ }
+
+ private int FF3(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + F3(b, c, d) + x + unchecked((int) 0x5c4dd124), s);
+ }
+
+ private int FF4(
+ int a,
+ int b,
+ int c,
+ int d,
+ int x,
+ int s)
+ {
+ return RL(a + F4(b, c, d) + x + unchecked((int) 0x50a28be6), s);
+ }
+
+ internal override void ProcessBlock()
+ {
+ int a, aa;
+ int b, bb;
+ int c, cc;
+ int d, dd;
+
+ a = aa = H0;
+ b = bb = H1;
+ c = cc = H2;
+ d = dd = H3;
+
+ //
+ // Round 1
+ //
+ a = F1(a, b, c, d, X[ 0], 11);
+ d = F1(d, a, b, c, X[ 1], 14);
+ c = F1(c, d, a, b, X[ 2], 15);
+ b = F1(b, c, d, a, X[ 3], 12);
+ a = F1(a, b, c, d, X[ 4], 5);
+ d = F1(d, a, b, c, X[ 5], 8);
+ c = F1(c, d, a, b, X[ 6], 7);
+ b = F1(b, c, d, a, X[ 7], 9);
+ a = F1(a, b, c, d, X[ 8], 11);
+ d = F1(d, a, b, c, X[ 9], 13);
+ c = F1(c, d, a, b, X[10], 14);
+ b = F1(b, c, d, a, X[11], 15);
+ a = F1(a, b, c, d, X[12], 6);
+ d = F1(d, a, b, c, X[13], 7);
+ c = F1(c, d, a, b, X[14], 9);
+ b = F1(b, c, d, a, X[15], 8);
+
+ //
+ // Round 2
+ //
+ a = F2(a, b, c, d, X[ 7], 7);
+ d = F2(d, a, b, c, X[ 4], 6);
+ c = F2(c, d, a, b, X[13], 8);
+ b = F2(b, c, d, a, X[ 1], 13);
+ a = F2(a, b, c, d, X[10], 11);
+ d = F2(d, a, b, c, X[ 6], 9);
+ c = F2(c, d, a, b, X[15], 7);
+ b = F2(b, c, d, a, X[ 3], 15);
+ a = F2(a, b, c, d, X[12], 7);
+ d = F2(d, a, b, c, X[ 0], 12);
+ c = F2(c, d, a, b, X[ 9], 15);
+ b = F2(b, c, d, a, X[ 5], 9);
+ a = F2(a, b, c, d, X[ 2], 11);
+ d = F2(d, a, b, c, X[14], 7);
+ c = F2(c, d, a, b, X[11], 13);
+ b = F2(b, c, d, a, X[ 8], 12);
+
+ //
+ // Round 3
+ //
+ a = F3(a, b, c, d, X[ 3], 11);
+ d = F3(d, a, b, c, X[10], 13);
+ c = F3(c, d, a, b, X[14], 6);
+ b = F3(b, c, d, a, X[ 4], 7);
+ a = F3(a, b, c, d, X[ 9], 14);
+ d = F3(d, a, b, c, X[15], 9);
+ c = F3(c, d, a, b, X[ 8], 13);
+ b = F3(b, c, d, a, X[ 1], 15);
+ a = F3(a, b, c, d, X[ 2], 14);
+ d = F3(d, a, b, c, X[ 7], 8);
+ c = F3(c, d, a, b, X[ 0], 13);
+ b = F3(b, c, d, a, X[ 6], 6);
+ a = F3(a, b, c, d, X[13], 5);
+ d = F3(d, a, b, c, X[11], 12);
+ c = F3(c, d, a, b, X[ 5], 7);
+ b = F3(b, c, d, a, X[12], 5);
+
+ //
+ // Round 4
+ //
+ a = F4(a, b, c, d, X[ 1], 11);
+ d = F4(d, a, b, c, X[ 9], 12);
+ c = F4(c, d, a, b, X[11], 14);
+ b = F4(b, c, d, a, X[10], 15);
+ a = F4(a, b, c, d, X[ 0], 14);
+ d = F4(d, a, b, c, X[ 8], 15);
+ c = F4(c, d, a, b, X[12], 9);
+ b = F4(b, c, d, a, X[ 4], 8);
+ a = F4(a, b, c, d, X[13], 9);
+ d = F4(d, a, b, c, X[ 3], 14);
+ c = F4(c, d, a, b, X[ 7], 5);
+ b = F4(b, c, d, a, X[15], 6);
+ a = F4(a, b, c, d, X[14], 8);
+ d = F4(d, a, b, c, X[ 5], 6);
+ c = F4(c, d, a, b, X[ 6], 5);
+ b = F4(b, c, d, a, X[ 2], 12);
+
+ //
+ // Parallel round 1
+ //
+ aa = FF4(aa, bb, cc, dd, X[ 5], 8);
+ dd = FF4(dd, aa, bb, cc, X[14], 9);
+ cc = FF4(cc, dd, aa, bb, X[ 7], 9);
+ bb = FF4(bb, cc, dd, aa, X[ 0], 11);
+ aa = FF4(aa, bb, cc, dd, X[ 9], 13);
+ dd = FF4(dd, aa, bb, cc, X[ 2], 15);
+ cc = FF4(cc, dd, aa, bb, X[11], 15);
+ bb = FF4(bb, cc, dd, aa, X[ 4], 5);
+ aa = FF4(aa, bb, cc, dd, X[13], 7);
+ dd = FF4(dd, aa, bb, cc, X[ 6], 7);
+ cc = FF4(cc, dd, aa, bb, X[15], 8);
+ bb = FF4(bb, cc, dd, aa, X[ 8], 11);
+ aa = FF4(aa, bb, cc, dd, X[ 1], 14);
+ dd = FF4(dd, aa, bb, cc, X[10], 14);
+ cc = FF4(cc, dd, aa, bb, X[ 3], 12);
+ bb = FF4(bb, cc, dd, aa, X[12], 6);
+
+ //
+ // Parallel round 2
+ //
+ aa = FF3(aa, bb, cc, dd, X[ 6], 9);
+ dd = FF3(dd, aa, bb, cc, X[11], 13);
+ cc = FF3(cc, dd, aa, bb, X[ 3], 15);
+ bb = FF3(bb, cc, dd, aa, X[ 7], 7);
+ aa = FF3(aa, bb, cc, dd, X[ 0], 12);
+ dd = FF3(dd, aa, bb, cc, X[13], 8);
+ cc = FF3(cc, dd, aa, bb, X[ 5], 9);
+ bb = FF3(bb, cc, dd, aa, X[10], 11);
+ aa = FF3(aa, bb, cc, dd, X[14], 7);
+ dd = FF3(dd, aa, bb, cc, X[15], 7);
+ cc = FF3(cc, dd, aa, bb, X[ 8], 12);
+ bb = FF3(bb, cc, dd, aa, X[12], 7);
+ aa = FF3(aa, bb, cc, dd, X[ 4], 6);
+ dd = FF3(dd, aa, bb, cc, X[ 9], 15);
+ cc = FF3(cc, dd, aa, bb, X[ 1], 13);
+ bb = FF3(bb, cc, dd, aa, X[ 2], 11);
+
+ //
+ // Parallel round 3
+ //
+ aa = FF2(aa, bb, cc, dd, X[15], 9);
+ dd = FF2(dd, aa, bb, cc, X[ 5], 7);
+ cc = FF2(cc, dd, aa, bb, X[ 1], 15);
+ bb = FF2(bb, cc, dd, aa, X[ 3], 11);
+ aa = FF2(aa, bb, cc, dd, X[ 7], 8);
+ dd = FF2(dd, aa, bb, cc, X[14], 6);
+ cc = FF2(cc, dd, aa, bb, X[ 6], 6);
+ bb = FF2(bb, cc, dd, aa, X[ 9], 14);
+ aa = FF2(aa, bb, cc, dd, X[11], 12);
+ dd = FF2(dd, aa, bb, cc, X[ 8], 13);
+ cc = FF2(cc, dd, aa, bb, X[12], 5);
+ bb = FF2(bb, cc, dd, aa, X[ 2], 14);
+ aa = FF2(aa, bb, cc, dd, X[10], 13);
+ dd = FF2(dd, aa, bb, cc, X[ 0], 13);
+ cc = FF2(cc, dd, aa, bb, X[ 4], 7);
+ bb = FF2(bb, cc, dd, aa, X[13], 5);
+
+ //
+ // Parallel round 4
+ //
+ aa = FF1(aa, bb, cc, dd, X[ 8], 15);
+ dd = FF1(dd, aa, bb, cc, X[ 6], 5);
+ cc = FF1(cc, dd, aa, bb, X[ 4], 8);
+ bb = FF1(bb, cc, dd, aa, X[ 1], 11);
+ aa = FF1(aa, bb, cc, dd, X[ 3], 14);
+ dd = FF1(dd, aa, bb, cc, X[11], 14);
+ cc = FF1(cc, dd, aa, bb, X[15], 6);
+ bb = FF1(bb, cc, dd, aa, X[ 0], 14);
+ aa = FF1(aa, bb, cc, dd, X[ 5], 6);
+ dd = FF1(dd, aa, bb, cc, X[12], 9);
+ cc = FF1(cc, dd, aa, bb, X[ 2], 12);
+ bb = FF1(bb, cc, dd, aa, X[13], 9);
+ aa = FF1(aa, bb, cc, dd, X[ 9], 12);
+ dd = FF1(dd, aa, bb, cc, X[ 7], 5);
+ cc = FF1(cc, dd, aa, bb, X[10], 15);
+ bb = FF1(bb, cc, dd, aa, X[14], 8);
+
+ dd += c + H1; // final result for H0
+
+ //
+ // combine the results
+ //
+ H1 = H2 + d + aa;
+ H2 = H3 + a + bb;
+ H3 = H0 + b + cc;
+ H0 = dd;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/digests/RipeMD160Digest.cs b/Crypto/src/crypto/digests/RipeMD160Digest.cs
new file mode 100644
index 000000000..8ce52ae58
--- /dev/null
+++ b/Crypto/src/crypto/digests/RipeMD160Digest.cs
@@ -0,0 +1,423 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * implementation of RipeMD see,
+ * http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html
+ */
+ public class RipeMD160Digest
+ : GeneralDigest
+ {
+ private const int DigestLength = 20;
+
+ private int H0, H1, H2, H3, H4; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public RipeMD160Digest()
+ {
+ Reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public RipeMD160Digest(RipeMD160Digest t) : base(t)
+ {
+ H0 = t.H0;
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "RIPEMD160"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ internal override void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8)
+ | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ internal override void ProcessLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)((ulong) bitLength >> 32);
+ }
+
+ private void UnpackWord(
+ int word,
+ byte[] outBytes,
+ int outOff)
+ {
+ outBytes[outOff] = (byte)word;
+ outBytes[outOff + 1] = (byte)((uint) word >> 8);
+ outBytes[outOff + 2] = (byte)((uint) word >> 16);
+ outBytes[outOff + 3] = (byte)((uint) word >> 24);
+ }
+
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ UnpackWord(H0, output, outOff);
+ UnpackWord(H1, output, outOff + 4);
+ UnpackWord(H2, output, outOff + 8);
+ UnpackWord(H3, output, outOff + 12);
+ UnpackWord(H4, output, outOff + 16);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public override void Reset()
+ {
+ base.Reset();
+
+ H0 = unchecked((int) 0x67452301);
+ H1 = unchecked((int) 0xefcdab89);
+ H2 = unchecked((int) 0x98badcfe);
+ H3 = unchecked((int) 0x10325476);
+ H4 = unchecked((int) 0xc3d2e1f0);
+
+ xOff = 0;
+
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RL(
+ int x,
+ int n)
+ {
+ return (x << n) | (int) ((uint) x >> (32 - n));
+ }
+
+ /*
+ * f1,f2,f3,f4,f5 are the basic RipeMD160 functions.
+ */
+
+ /*
+ * rounds 0-15
+ */
+ private int F1(
+ int x,
+ int y,
+ int z)
+ {
+ return x ^ y ^ z;
+ }
+
+ /*
+ * rounds 16-31
+ */
+ private int F2(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & y) | (~x & z);
+ }
+
+ /*
+ * rounds 32-47
+ */
+ private int F3(
+ int x,
+ int y,
+ int z)
+ {
+ return (x | ~y) ^ z;
+ }
+
+ /*
+ * rounds 48-63
+ */
+ private int F4(
+ int x,
+ int y,
+ int z)
+ {
+ return (x & z) | (y & ~z);
+ }
+
+ /*
+ * rounds 64-79
+ */
+ private int F5(
+ int x,
+ int y,
+ int z)
+ {
+ return x ^ (y | ~z);
+ }
+
+ internal override void ProcessBlock()
+ {
+ int a, aa;
+ int b, bb;
+ int c, cc;
+ int d, dd;
+ int e, ee;
+
+ a = aa = H0;
+ b = bb = H1;
+ c = cc = H2;
+ d = dd = H3;
+ e = ee = H4;
+
+ //
+ // Rounds 1 - 16
+ //
+ // left
+ a = RL(a + F1(b,c,d) + X[ 0], 11) + e; c = RL(c, 10);
+ e = RL(e + F1(a,b,c) + X[ 1], 14) + d; b = RL(b, 10);
+ d = RL(d + F1(e,a,b) + X[ 2], 15) + c; a = RL(a, 10);
+ c = RL(c + F1(d,e,a) + X[ 3], 12) + b; e = RL(e, 10);
+ b = RL(b + F1(c,d,e) + X[ 4], 5) + a; d = RL(d, 10);
+ a = RL(a + F1(b,c,d) + X[ 5], 8) + e; c = RL(c, 10);
+ e = RL(e + F1(a,b,c) + X[ 6], 7) + d; b = RL(b, 10);
+ d = RL(d + F1(e,a,b) + X[ 7], 9) + c; a = RL(a, 10);
+ c = RL(c + F1(d,e,a) + X[ 8], 11) + b; e = RL(e, 10);
+ b = RL(b + F1(c,d,e) + X[ 9], 13) + a; d = RL(d, 10);
+ a = RL(a + F1(b,c,d) + X[10], 14) + e; c = RL(c, 10);
+ e = RL(e + F1(a,b,c) + X[11], 15) + d; b = RL(b, 10);
+ d = RL(d + F1(e,a,b) + X[12], 6) + c; a = RL(a, 10);
+ c = RL(c + F1(d,e,a) + X[13], 7) + b; e = RL(e, 10);
+ b = RL(b + F1(c,d,e) + X[14], 9) + a; d = RL(d, 10);
+ a = RL(a + F1(b,c,d) + X[15], 8) + e; c = RL(c, 10);
+
+ // right
+ aa = RL(aa + F5(bb,cc,dd) + X[ 5] + unchecked((int) 0x50a28be6), 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F5(aa,bb,cc) + X[14] + unchecked((int) 0x50a28be6), 9) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F5(ee,aa,bb) + X[ 7] + unchecked((int) 0x50a28be6), 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F5(dd,ee,aa) + X[ 0] + unchecked((int) 0x50a28be6), 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F5(cc,dd,ee) + X[ 9] + unchecked((int) 0x50a28be6), 13) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F5(bb,cc,dd) + X[ 2] + unchecked((int) 0x50a28be6), 15) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F5(aa,bb,cc) + X[11] + unchecked((int) 0x50a28be6), 15) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F5(ee,aa,bb) + X[ 4] + unchecked((int) 0x50a28be6), 5) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F5(dd,ee,aa) + X[13] + unchecked((int) 0x50a28be6), 7) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F5(cc,dd,ee) + X[ 6] + unchecked((int) 0x50a28be6), 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F5(bb,cc,dd) + X[15] + unchecked((int) 0x50a28be6), 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F5(aa,bb,cc) + X[ 8] + unchecked((int) 0x50a28be6), 11) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F5(ee,aa,bb) + X[ 1] + unchecked((int) 0x50a28be6), 14) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F5(dd,ee,aa) + X[10] + unchecked((int) 0x50a28be6), 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F5(cc,dd,ee) + X[ 3] + unchecked((int) 0x50a28be6), 12) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F5(bb,cc,dd) + X[12] + unchecked((int) 0x50a28be6), 6) + ee; cc = RL(cc, 10);
+
+ //
+ // Rounds 16-31
+ //
+ // left
+ e = RL(e + F2(a,b,c) + X[ 7] + unchecked((int) 0x5a827999), 7) + d; b = RL(b, 10);
+ d = RL(d + F2(e,a,b) + X[ 4] + unchecked((int) 0x5a827999), 6) + c; a = RL(a, 10);
+ c = RL(c + F2(d,e,a) + X[13] + unchecked((int) 0x5a827999), 8) + b; e = RL(e, 10);
+ b = RL(b + F2(c,d,e) + X[ 1] + unchecked((int) 0x5a827999), 13) + a; d = RL(d, 10);
+ a = RL(a + F2(b,c,d) + X[10] + unchecked((int) 0x5a827999), 11) + e; c = RL(c, 10);
+ e = RL(e + F2(a,b,c) + X[ 6] + unchecked((int) 0x5a827999), 9) + d; b = RL(b, 10);
+ d = RL(d + F2(e,a,b) + X[15] + unchecked((int) 0x5a827999), 7) + c; a = RL(a, 10);
+ c = RL(c + F2(d,e,a) + X[ 3] + unchecked((int) 0x5a827999), 15) + b; e = RL(e, 10);
+ b = RL(b + F2(c,d,e) + X[12] + unchecked((int) 0x5a827999), 7) + a; d = RL(d, 10);
+ a = RL(a + F2(b,c,d) + X[ 0] + unchecked((int) 0x5a827999), 12) + e; c = RL(c, 10);
+ e = RL(e + F2(a,b,c) + X[ 9] + unchecked((int) 0x5a827999), 15) + d; b = RL(b, 10);
+ d = RL(d + F2(e,a,b) + X[ 5] + unchecked((int) 0x5a827999), 9) + c; a = RL(a, 10);
+ c = RL(c + F2(d,e,a) + X[ 2] + unchecked((int) 0x5a827999), 11) + b; e = RL(e, 10);
+ b = RL(b + F2(c,d,e) + X[14] + unchecked((int) 0x5a827999), 7) + a; d = RL(d, 10);
+ a = RL(a + F2(b,c,d) + X[11] + unchecked((int) 0x5a827999), 13) + e; c = RL(c, 10);
+ e = RL(e + F2(a,b,c) + X[ 8] + unchecked((int) 0x5a827999), 12) + d; b = RL(b, 10);
+
+ // right
+ ee = RL(ee + F4(aa,bb,cc) + X[ 6] + unchecked((int) 0x5c4dd124), 9) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F4(ee,aa,bb) + X[11] + unchecked((int) 0x5c4dd124), 13) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F4(dd,ee,aa) + X[ 3] + unchecked((int) 0x5c4dd124), 15) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F4(cc,dd,ee) + X[ 7] + unchecked((int) 0x5c4dd124), 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F4(bb,cc,dd) + X[ 0] + unchecked((int) 0x5c4dd124), 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F4(aa,bb,cc) + X[13] + unchecked((int) 0x5c4dd124), 8) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F4(ee,aa,bb) + X[ 5] + unchecked((int) 0x5c4dd124), 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F4(dd,ee,aa) + X[10] + unchecked((int) 0x5c4dd124), 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F4(cc,dd,ee) + X[14] + unchecked((int) 0x5c4dd124), 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F4(bb,cc,dd) + X[15] + unchecked((int) 0x5c4dd124), 7) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F4(aa,bb,cc) + X[ 8] + unchecked((int) 0x5c4dd124), 12) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F4(ee,aa,bb) + X[12] + unchecked((int) 0x5c4dd124), 7) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F4(dd,ee,aa) + X[ 4] + unchecked((int) 0x5c4dd124), 6) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F4(cc,dd,ee) + X[ 9] + unchecked((int) 0x5c4dd124), 15) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F4(bb,cc,dd) + X[ 1] + unchecked((int) 0x5c4dd124), 13) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F4(aa,bb,cc) + X[ 2] + unchecked((int) 0x5c4dd124), 11) + dd; bb = RL(bb, 10);
+
+ //
+ // Rounds 32-47
+ //
+ // left
+ d = RL(d + F3(e,a,b) + X[ 3] + unchecked((int) 0x6ed9eba1), 11) + c; a = RL(a, 10);
+ c = RL(c + F3(d,e,a) + X[10] + unchecked((int) 0x6ed9eba1), 13) + b; e = RL(e, 10);
+ b = RL(b + F3(c,d,e) + X[14] + unchecked((int) 0x6ed9eba1), 6) + a; d = RL(d, 10);
+ a = RL(a + F3(b,c,d) + X[ 4] + unchecked((int) 0x6ed9eba1), 7) + e; c = RL(c, 10);
+ e = RL(e + F3(a,b,c) + X[ 9] + unchecked((int) 0x6ed9eba1), 14) + d; b = RL(b, 10);
+ d = RL(d + F3(e,a,b) + X[15] + unchecked((int) 0x6ed9eba1), 9) + c; a = RL(a, 10);
+ c = RL(c + F3(d,e,a) + X[ 8] + unchecked((int) 0x6ed9eba1), 13) + b; e = RL(e, 10);
+ b = RL(b + F3(c,d,e) + X[ 1] + unchecked((int) 0x6ed9eba1), 15) + a; d = RL(d, 10);
+ a = RL(a + F3(b,c,d) + X[ 2] + unchecked((int) 0x6ed9eba1), 14) + e; c = RL(c, 10);
+ e = RL(e + F3(a,b,c) + X[ 7] + unchecked((int) 0x6ed9eba1), 8) + d; b = RL(b, 10);
+ d = RL(d + F3(e,a,b) + X[ 0] + unchecked((int) 0x6ed9eba1), 13) + c; a = RL(a, 10);
+ c = RL(c + F3(d,e,a) + X[ 6] + unchecked((int) 0x6ed9eba1), 6) + b; e = RL(e, 10);
+ b = RL(b + F3(c,d,e) + X[13] + unchecked((int) 0x6ed9eba1), 5) + a; d = RL(d, 10);
+ a = RL(a + F3(b,c,d) + X[11] + unchecked((int) 0x6ed9eba1), 12) + e; c = RL(c, 10);
+ e = RL(e + F3(a,b,c) + X[ 5] + unchecked((int) 0x6ed9eba1), 7) + d; b = RL(b, 10);
+ d = RL(d + F3(e,a,b) + X[12] + unchecked((int) 0x6ed9eba1), 5) + c; a = RL(a, 10);
+
+ // right
+ dd = RL(dd + F3(ee,aa,bb) + X[15] + unchecked((int) 0x6d703ef3), 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F3(dd,ee,aa) + X[ 5] + unchecked((int) 0x6d703ef3), 7) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F3(cc,dd,ee) + X[ 1] + unchecked((int) 0x6d703ef3), 15) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F3(bb,cc,dd) + X[ 3] + unchecked((int) 0x6d703ef3), 11) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F3(aa,bb,cc) + X[ 7] + unchecked((int) 0x6d703ef3), 8) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F3(ee,aa,bb) + X[14] + unchecked((int) 0x6d703ef3), 6) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F3(dd,ee,aa) + X[ 6] + unchecked((int) 0x6d703ef3), 6) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F3(cc,dd,ee) + X[ 9] + unchecked((int) 0x6d703ef3), 14) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F3(bb,cc,dd) + X[11] + unchecked((int) 0x6d703ef3), 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F3(aa,bb,cc) + X[ 8] + unchecked((int) 0x6d703ef3), 13) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F3(ee,aa,bb) + X[12] + unchecked((int) 0x6d703ef3), 5) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F3(dd,ee,aa) + X[ 2] + unchecked((int) 0x6d703ef3), 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F3(cc,dd,ee) + X[10] + unchecked((int) 0x6d703ef3), 13) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F3(bb,cc,dd) + X[ 0] + unchecked((int) 0x6d703ef3), 13) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F3(aa,bb,cc) + X[ 4] + unchecked((int) 0x6d703ef3), 7) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F3(ee,aa,bb) + X[13] + unchecked((int) 0x6d703ef3), 5) + cc; aa = RL(aa, 10);
+
+ //
+ // Rounds 48-63
+ //
+ // left
+ c = RL(c + F4(d,e,a) + X[ 1] + unchecked((int) 0x8f1bbcdc), 11) + b; e = RL(e, 10);
+ b = RL(b + F4(c,d,e) + X[ 9] + unchecked((int) 0x8f1bbcdc), 12) + a; d = RL(d, 10);
+ a = RL(a + F4(b,c,d) + X[11] + unchecked((int) 0x8f1bbcdc), 14) + e; c = RL(c, 10);
+ e = RL(e + F4(a,b,c) + X[10] + unchecked((int) 0x8f1bbcdc), 15) + d; b = RL(b, 10);
+ d = RL(d + F4(e,a,b) + X[ 0] + unchecked((int) 0x8f1bbcdc), 14) + c; a = RL(a, 10);
+ c = RL(c + F4(d,e,a) + X[ 8] + unchecked((int) 0x8f1bbcdc), 15) + b; e = RL(e, 10);
+ b = RL(b + F4(c,d,e) + X[12] + unchecked((int) 0x8f1bbcdc), 9) + a; d = RL(d, 10);
+ a = RL(a + F4(b,c,d) + X[ 4] + unchecked((int) 0x8f1bbcdc), 8) + e; c = RL(c, 10);
+ e = RL(e + F4(a,b,c) + X[13] + unchecked((int) 0x8f1bbcdc), 9) + d; b = RL(b, 10);
+ d = RL(d + F4(e,a,b) + X[ 3] + unchecked((int) 0x8f1bbcdc), 14) + c; a = RL(a, 10);
+ c = RL(c + F4(d,e,a) + X[ 7] + unchecked((int) 0x8f1bbcdc), 5) + b; e = RL(e, 10);
+ b = RL(b + F4(c,d,e) + X[15] + unchecked((int) 0x8f1bbcdc), 6) + a; d = RL(d, 10);
+ a = RL(a + F4(b,c,d) + X[14] + unchecked((int) 0x8f1bbcdc), 8) + e; c = RL(c, 10);
+ e = RL(e + F4(a,b,c) + X[ 5] + unchecked((int) 0x8f1bbcdc), 6) + d; b = RL(b, 10);
+ d = RL(d + F4(e,a,b) + X[ 6] + unchecked((int) 0x8f1bbcdc), 5) + c; a = RL(a, 10);
+ c = RL(c + F4(d,e,a) + X[ 2] + unchecked((int) 0x8f1bbcdc), 12) + b; e = RL(e, 10);
+
+ // right
+ cc = RL(cc + F2(dd,ee,aa) + X[ 8] + unchecked((int) 0x7a6d76e9), 15) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F2(cc,dd,ee) + X[ 6] + unchecked((int) 0x7a6d76e9), 5) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F2(bb,cc,dd) + X[ 4] + unchecked((int) 0x7a6d76e9), 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F2(aa,bb,cc) + X[ 1] + unchecked((int) 0x7a6d76e9), 11) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F2(ee,aa,bb) + X[ 3] + unchecked((int) 0x7a6d76e9), 14) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F2(dd,ee,aa) + X[11] + unchecked((int) 0x7a6d76e9), 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F2(cc,dd,ee) + X[15] + unchecked((int) 0x7a6d76e9), 6) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F2(bb,cc,dd) + X[ 0] + unchecked((int) 0x7a6d76e9), 14) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F2(aa,bb,cc) + X[ 5] + unchecked((int) 0x7a6d76e9), 6) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F2(ee,aa,bb) + X[12] + unchecked((int) 0x7a6d76e9), 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F2(dd,ee,aa) + X[ 2] + unchecked((int) 0x7a6d76e9), 12) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F2(cc,dd,ee) + X[13] + unchecked((int) 0x7a6d76e9), 9) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F2(bb,cc,dd) + X[ 9] + unchecked((int) 0x7a6d76e9), 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F2(aa,bb,cc) + X[ 7] + unchecked((int) 0x7a6d76e9), 5) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F2(ee,aa,bb) + X[10] + unchecked((int) 0x7a6d76e9), 15) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F2(dd,ee,aa) + X[14] + unchecked((int) 0x7a6d76e9), 8) + bb; ee = RL(ee, 10);
+
+ //
+ // Rounds 64-79
+ //
+ // left
+ b = RL(b + F5(c,d,e) + X[ 4] + unchecked((int) 0xa953fd4e), 9) + a; d = RL(d, 10);
+ a = RL(a + F5(b,c,d) + X[ 0] + unchecked((int) 0xa953fd4e), 15) + e; c = RL(c, 10);
+ e = RL(e + F5(a,b,c) + X[ 5] + unchecked((int) 0xa953fd4e), 5) + d; b = RL(b, 10);
+ d = RL(d + F5(e,a,b) + X[ 9] + unchecked((int) 0xa953fd4e), 11) + c; a = RL(a, 10);
+ c = RL(c + F5(d,e,a) + X[ 7] + unchecked((int) 0xa953fd4e), 6) + b; e = RL(e, 10);
+ b = RL(b + F5(c,d,e) + X[12] + unchecked((int) 0xa953fd4e), 8) + a; d = RL(d, 10);
+ a = RL(a + F5(b,c,d) + X[ 2] + unchecked((int) 0xa953fd4e), 13) + e; c = RL(c, 10);
+ e = RL(e + F5(a,b,c) + X[10] + unchecked((int) 0xa953fd4e), 12) + d; b = RL(b, 10);
+ d = RL(d + F5(e,a,b) + X[14] + unchecked((int) 0xa953fd4e), 5) + c; a = RL(a, 10);
+ c = RL(c + F5(d,e,a) + X[ 1] + unchecked((int) 0xa953fd4e), 12) + b; e = RL(e, 10);
+ b = RL(b + F5(c,d,e) + X[ 3] + unchecked((int) 0xa953fd4e), 13) + a; d = RL(d, 10);
+ a = RL(a + F5(b,c,d) + X[ 8] + unchecked((int) 0xa953fd4e), 14) + e; c = RL(c, 10);
+ e = RL(e + F5(a,b,c) + X[11] + unchecked((int) 0xa953fd4e), 11) + d; b = RL(b, 10);
+ d = RL(d + F5(e,a,b) + X[ 6] + unchecked((int) 0xa953fd4e), 8) + c; a = RL(a, 10);
+ c = RL(c + F5(d,e,a) + X[15] + unchecked((int) 0xa953fd4e), 5) + b; e = RL(e, 10);
+ b = RL(b + F5(c,d,e) + X[13] + unchecked((int) 0xa953fd4e), 6) + a; d = RL(d, 10);
+
+ // right
+ bb = RL(bb + F1(cc,dd,ee) + X[12], 8) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F1(bb,cc,dd) + X[15], 5) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F1(aa,bb,cc) + X[10], 12) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F1(ee,aa,bb) + X[ 4], 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F1(dd,ee,aa) + X[ 1], 12) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F1(cc,dd,ee) + X[ 5], 5) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F1(bb,cc,dd) + X[ 8], 14) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F1(aa,bb,cc) + X[ 7], 6) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F1(ee,aa,bb) + X[ 6], 8) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F1(dd,ee,aa) + X[ 2], 13) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F1(cc,dd,ee) + X[13], 6) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F1(bb,cc,dd) + X[14], 5) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F1(aa,bb,cc) + X[ 0], 15) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F1(ee,aa,bb) + X[ 3], 13) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F1(dd,ee,aa) + X[ 9], 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F1(cc,dd,ee) + X[11], 11) + aa; dd = RL(dd, 10);
+
+ dd += c + H1;
+ H1 = H2 + d + ee;
+ H2 = H3 + e + aa;
+ H3 = H4 + a + bb;
+ H4 = H0 + b + cc;
+ H0 = dd;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/digests/RipeMD256Digest.cs b/Crypto/src/crypto/digests/RipeMD256Digest.cs
new file mode 100644
index 000000000..950e94f80
--- /dev/null
+++ b/Crypto/src/crypto/digests/RipeMD256Digest.cs
@@ -0,0 +1,409 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /// <remarks>
+ /// <p>Implementation of RipeMD256.</p>
+ /// <p><b>Note:</b> this algorithm offers the same level of security as RipeMD128.</p>
+ /// </remarks>
+ public class RipeMD256Digest
+ : GeneralDigest
+ {
+ public override string AlgorithmName
+ {
+ get { return "RIPEMD256"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ private const int DigestLength = 32;
+
+ private int H0, H1, H2, H3, H4, H5, H6, H7; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /// <summary> Standard constructor</summary>
+ public RipeMD256Digest()
+ {
+ Reset();
+ }
+
+ /// <summary> Copy constructor. This will copy the state of the provided
+ /// message digest.
+ /// </summary>
+ public RipeMD256Digest(RipeMD256Digest t):base(t)
+ {
+
+ H0 = t.H0;
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ }
+
+ internal override void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8)
+ | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ internal override void ProcessLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)((ulong)bitLength >> 32);
+ }
+
+ private void UnpackWord(
+ int word,
+ byte[] outBytes,
+ int outOff)
+ {
+ outBytes[outOff] = (byte)(uint)word;
+ outBytes[outOff + 1] = (byte)((uint)word >> 8);
+ outBytes[outOff + 2] = (byte)((uint)word >> 16);
+ outBytes[outOff + 3] = (byte)((uint)word >> 24);
+ }
+
+ public override int DoFinal(byte[] output, int outOff)
+ {
+ Finish();
+
+ UnpackWord(H0, output, outOff);
+ UnpackWord(H1, output, outOff + 4);
+ UnpackWord(H2, output, outOff + 8);
+ UnpackWord(H3, output, outOff + 12);
+ UnpackWord(H4, output, outOff + 16);
+ UnpackWord(H5, output, outOff + 20);
+ UnpackWord(H6, output, outOff + 24);
+ UnpackWord(H7, output, outOff + 28);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /// <summary> reset the chaining variables to the IV values.</summary>
+ public override void Reset()
+ {
+ base.Reset();
+
+ H0 = unchecked((int)0x67452301);
+ H1 = unchecked((int)0xefcdab89);
+ H2 = unchecked((int)0x98badcfe);
+ H3 = unchecked((int)0x10325476);
+ H4 = unchecked((int)0x76543210);
+ H5 = unchecked((int)0xFEDCBA98);
+ H6 = unchecked((int)0x89ABCDEF);
+ H7 = unchecked((int)0x01234567);
+
+ xOff = 0;
+
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RL(
+ int x,
+ int n)
+ {
+ return (x << n) | (int)((uint)x >> (32 - n));
+ }
+
+ /*
+ * f1,f2,f3,f4 are the basic RipeMD128 functions.
+ */
+
+ /*
+ * F
+ */
+ private int F1(int x, int y, int z)
+ {
+ return x ^ y ^ z;
+ }
+
+ /*
+ * G
+ */
+ private int F2(int x, int y, int z)
+ {
+ return (x & y) | (~ x & z);
+ }
+
+ /*
+ * H
+ */
+ private int F3(int x, int y, int z)
+ {
+ return (x | ~ y) ^ z;
+ }
+
+ /*
+ * I
+ */
+ private int F4(int x, int y, int z)
+ {
+ return (x & z) | (y & ~ z);
+ }
+
+ private int F1(int a, int b, int c, int d, int x, int s)
+ {
+ return RL(a + F1(b, c, d) + x, s);
+ }
+
+ private int F2(int a, int b, int c, int d, int x, int s)
+ {
+ return RL(a + F2(b, c, d) + x + unchecked((int)0x5a827999), s);
+ }
+
+ private int F3(int a, int b, int c, int d, int x, int s)
+ {
+ return RL(a + F3(b, c, d) + x + unchecked((int)0x6ed9eba1), s);
+ }
+
+ private int F4(int a, int b, int c, int d, int x, int s)
+ {
+ return RL(a + F4(b, c, d) + x + unchecked((int)0x8f1bbcdc), s);
+ }
+
+ private int FF1(int a, int b, int c, int d, int x, int s)
+ {
+ return RL(a + F1(b, c, d) + x, s);
+ }
+
+ private int FF2(int a, int b, int c, int d, int x, int s)
+ {
+ return RL(a + F2(b, c, d) + x + unchecked((int)0x6d703ef3), s);
+ }
+
+ private int FF3(int a, int b, int c, int d, int x, int s)
+ {
+ return RL(a + F3(b, c, d) + x + unchecked((int)0x5c4dd124), s);
+ }
+
+ private int FF4(int a, int b, int c, int d, int x, int s)
+ {
+ return RL(a + F4(b, c, d) + x + unchecked((int)0x50a28be6), s);
+ }
+
+ internal override void ProcessBlock()
+ {
+ int a, aa;
+ int b, bb;
+ int c, cc;
+ int d, dd;
+ int t;
+
+ a = H0;
+ b = H1;
+ c = H2;
+ d = H3;
+ aa = H4;
+ bb = H5;
+ cc = H6;
+ dd = H7;
+
+ //
+ // Round 1
+ //
+
+ a = F1(a, b, c, d, X[0], 11);
+ d = F1(d, a, b, c, X[1], 14);
+ c = F1(c, d, a, b, X[2], 15);
+ b = F1(b, c, d, a, X[3], 12);
+ a = F1(a, b, c, d, X[4], 5);
+ d = F1(d, a, b, c, X[5], 8);
+ c = F1(c, d, a, b, X[6], 7);
+ b = F1(b, c, d, a, X[7], 9);
+ a = F1(a, b, c, d, X[8], 11);
+ d = F1(d, a, b, c, X[9], 13);
+ c = F1(c, d, a, b, X[10], 14);
+ b = F1(b, c, d, a, X[11], 15);
+ a = F1(a, b, c, d, X[12], 6);
+ d = F1(d, a, b, c, X[13], 7);
+ c = F1(c, d, a, b, X[14], 9);
+ b = F1(b, c, d, a, X[15], 8);
+
+ aa = FF4(aa, bb, cc, dd, X[5], 8);
+ dd = FF4(dd, aa, bb, cc, X[14], 9);
+ cc = FF4(cc, dd, aa, bb, X[7], 9);
+ bb = FF4(bb, cc, dd, aa, X[0], 11);
+ aa = FF4(aa, bb, cc, dd, X[9], 13);
+ dd = FF4(dd, aa, bb, cc, X[2], 15);
+ cc = FF4(cc, dd, aa, bb, X[11], 15);
+ bb = FF4(bb, cc, dd, aa, X[4], 5);
+ aa = FF4(aa, bb, cc, dd, X[13], 7);
+ dd = FF4(dd, aa, bb, cc, X[6], 7);
+ cc = FF4(cc, dd, aa, bb, X[15], 8);
+ bb = FF4(bb, cc, dd, aa, X[8], 11);
+ aa = FF4(aa, bb, cc, dd, X[1], 14);
+ dd = FF4(dd, aa, bb, cc, X[10], 14);
+ cc = FF4(cc, dd, aa, bb, X[3], 12);
+ bb = FF4(bb, cc, dd, aa, X[12], 6);
+
+ t = a; a = aa; aa = t;
+
+ //
+ // Round 2
+ //
+ a = F2(a, b, c, d, X[7], 7);
+ d = F2(d, a, b, c, X[4], 6);
+ c = F2(c, d, a, b, X[13], 8);
+ b = F2(b, c, d, a, X[1], 13);
+ a = F2(a, b, c, d, X[10], 11);
+ d = F2(d, a, b, c, X[6], 9);
+ c = F2(c, d, a, b, X[15], 7);
+ b = F2(b, c, d, a, X[3], 15);
+ a = F2(a, b, c, d, X[12], 7);
+ d = F2(d, a, b, c, X[0], 12);
+ c = F2(c, d, a, b, X[9], 15);
+ b = F2(b, c, d, a, X[5], 9);
+ a = F2(a, b, c, d, X[2], 11);
+ d = F2(d, a, b, c, X[14], 7);
+ c = F2(c, d, a, b, X[11], 13);
+ b = F2(b, c, d, a, X[8], 12);
+
+ aa = FF3(aa, bb, cc, dd, X[6], 9);
+ dd = FF3(dd, aa, bb, cc, X[11], 13);
+ cc = FF3(cc, dd, aa, bb, X[3], 15);
+ bb = FF3(bb, cc, dd, aa, X[7], 7);
+ aa = FF3(aa, bb, cc, dd, X[0], 12);
+ dd = FF3(dd, aa, bb, cc, X[13], 8);
+ cc = FF3(cc, dd, aa, bb, X[5], 9);
+ bb = FF3(bb, cc, dd, aa, X[10], 11);
+ aa = FF3(aa, bb, cc, dd, X[14], 7);
+ dd = FF3(dd, aa, bb, cc, X[15], 7);
+ cc = FF3(cc, dd, aa, bb, X[8], 12);
+ bb = FF3(bb, cc, dd, aa, X[12], 7);
+ aa = FF3(aa, bb, cc, dd, X[4], 6);
+ dd = FF3(dd, aa, bb, cc, X[9], 15);
+ cc = FF3(cc, dd, aa, bb, X[1], 13);
+ bb = FF3(bb, cc, dd, aa, X[2], 11);
+
+ t = b; b = bb; bb = t;
+
+ //
+ // Round 3
+ //
+ a = F3(a, b, c, d, X[3], 11);
+ d = F3(d, a, b, c, X[10], 13);
+ c = F3(c, d, a, b, X[14], 6);
+ b = F3(b, c, d, a, X[4], 7);
+ a = F3(a, b, c, d, X[9], 14);
+ d = F3(d, a, b, c, X[15], 9);
+ c = F3(c, d, a, b, X[8], 13);
+ b = F3(b, c, d, a, X[1], 15);
+ a = F3(a, b, c, d, X[2], 14);
+ d = F3(d, a, b, c, X[7], 8);
+ c = F3(c, d, a, b, X[0], 13);
+ b = F3(b, c, d, a, X[6], 6);
+ a = F3(a, b, c, d, X[13], 5);
+ d = F3(d, a, b, c, X[11], 12);
+ c = F3(c, d, a, b, X[5], 7);
+ b = F3(b, c, d, a, X[12], 5);
+
+ aa = FF2(aa, bb, cc, dd, X[15], 9);
+ dd = FF2(dd, aa, bb, cc, X[5], 7);
+ cc = FF2(cc, dd, aa, bb, X[1], 15);
+ bb = FF2(bb, cc, dd, aa, X[3], 11);
+ aa = FF2(aa, bb, cc, dd, X[7], 8);
+ dd = FF2(dd, aa, bb, cc, X[14], 6);
+ cc = FF2(cc, dd, aa, bb, X[6], 6);
+ bb = FF2(bb, cc, dd, aa, X[9], 14);
+ aa = FF2(aa, bb, cc, dd, X[11], 12);
+ dd = FF2(dd, aa, bb, cc, X[8], 13);
+ cc = FF2(cc, dd, aa, bb, X[12], 5);
+ bb = FF2(bb, cc, dd, aa, X[2], 14);
+ aa = FF2(aa, bb, cc, dd, X[10], 13);
+ dd = FF2(dd, aa, bb, cc, X[0], 13);
+ cc = FF2(cc, dd, aa, bb, X[4], 7);
+ bb = FF2(bb, cc, dd, aa, X[13], 5);
+
+ t = c; c = cc; cc = t;
+
+ //
+ // Round 4
+ //
+ a = F4(a, b, c, d, X[1], 11);
+ d = F4(d, a, b, c, X[9], 12);
+ c = F4(c, d, a, b, X[11], 14);
+ b = F4(b, c, d, a, X[10], 15);
+ a = F4(a, b, c, d, X[0], 14);
+ d = F4(d, a, b, c, X[8], 15);
+ c = F4(c, d, a, b, X[12], 9);
+ b = F4(b, c, d, a, X[4], 8);
+ a = F4(a, b, c, d, X[13], 9);
+ d = F4(d, a, b, c, X[3], 14);
+ c = F4(c, d, a, b, X[7], 5);
+ b = F4(b, c, d, a, X[15], 6);
+ a = F4(a, b, c, d, X[14], 8);
+ d = F4(d, a, b, c, X[5], 6);
+ c = F4(c, d, a, b, X[6], 5);
+ b = F4(b, c, d, a, X[2], 12);
+
+ aa = FF1(aa, bb, cc, dd, X[8], 15);
+ dd = FF1(dd, aa, bb, cc, X[6], 5);
+ cc = FF1(cc, dd, aa, bb, X[4], 8);
+ bb = FF1(bb, cc, dd, aa, X[1], 11);
+ aa = FF1(aa, bb, cc, dd, X[3], 14);
+ dd = FF1(dd, aa, bb, cc, X[11], 14);
+ cc = FF1(cc, dd, aa, bb, X[15], 6);
+ bb = FF1(bb, cc, dd, aa, X[0], 14);
+ aa = FF1(aa, bb, cc, dd, X[5], 6);
+ dd = FF1(dd, aa, bb, cc, X[12], 9);
+ cc = FF1(cc, dd, aa, bb, X[2], 12);
+ bb = FF1(bb, cc, dd, aa, X[13], 9);
+ aa = FF1(aa, bb, cc, dd, X[9], 12);
+ dd = FF1(dd, aa, bb, cc, X[7], 5);
+ cc = FF1(cc, dd, aa, bb, X[10], 15);
+ bb = FF1(bb, cc, dd, aa, X[14], 8);
+
+ t = d; d = dd; dd = t;
+
+ H0 += a;
+ H1 += b;
+ H2 += c;
+ H3 += d;
+ H4 += aa;
+ H5 += bb;
+ H6 += cc;
+ H7 += dd;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/digests/RipeMD320Digest.cs b/Crypto/src/crypto/digests/RipeMD320Digest.cs
new file mode 100644
index 000000000..25c74baef
--- /dev/null
+++ b/Crypto/src/crypto/digests/RipeMD320Digest.cs
@@ -0,0 +1,438 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /// <remarks>
+ /// <p>Implementation of RipeMD 320.</p>
+ /// <p><b>Note:</b> this algorithm offers the same level of security as RipeMD160.</p>
+ /// </remarks>
+ public class RipeMD320Digest
+ : GeneralDigest
+ {
+ public override string AlgorithmName
+ {
+ get { return "RIPEMD320"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ private const int DigestLength = 40;
+
+ private int H0, H1, H2, H3, H4, H5, H6, H7, H8, H9; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /// <summary> Standard constructor</summary>
+ public RipeMD320Digest()
+ {
+ Reset();
+ }
+
+ /// <summary> Copy constructor. This will copy the state of the provided
+ /// message digest.
+ /// </summary>
+ public RipeMD320Digest(RipeMD320Digest t)
+ : base(t)
+ {
+
+ H0 = t.H0;
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+ H8 = t.H8;
+ H9 = t.H9;
+
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ }
+
+ internal override void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8)
+ | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ internal override void ProcessLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)((ulong)bitLength >> 32);
+ }
+
+ private void UnpackWord(
+ int word,
+ byte[] outBytes,
+ int outOff)
+ {
+ outBytes[outOff] = (byte)word;
+ outBytes[outOff + 1] = (byte)((uint)word >> 8);
+ outBytes[outOff + 2] = (byte)((uint)word >> 16);
+ outBytes[outOff + 3] = (byte)((uint)word >> 24);
+ }
+
+ public override int DoFinal(byte[] output, int outOff)
+ {
+ Finish();
+
+ UnpackWord(H0, output, outOff);
+ UnpackWord(H1, output, outOff + 4);
+ UnpackWord(H2, output, outOff + 8);
+ UnpackWord(H3, output, outOff + 12);
+ UnpackWord(H4, output, outOff + 16);
+ UnpackWord(H5, output, outOff + 20);
+ UnpackWord(H6, output, outOff + 24);
+ UnpackWord(H7, output, outOff + 28);
+ UnpackWord(H8, output, outOff + 32);
+ UnpackWord(H9, output, outOff + 36);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /// <summary> reset the chaining variables to the IV values.</summary>
+ public override void Reset()
+ {
+ base.Reset();
+
+ H0 = unchecked((int) 0x67452301);
+ H1 = unchecked((int) 0xefcdab89);
+ H2 = unchecked((int) 0x98badcfe);
+ H3 = unchecked((int) 0x10325476);
+ H4 = unchecked((int) 0xc3d2e1f0);
+ H5 = unchecked((int) 0x76543210);
+ H6 = unchecked((int) 0xFEDCBA98);
+ H7 = unchecked((int) 0x89ABCDEF);
+ H8 = unchecked((int) 0x01234567);
+ H9 = unchecked((int) 0x3C2D1E0F);
+
+ xOff = 0;
+
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int RL(
+ int x,
+ int n)
+ {
+ return (x << n) | (int)(((uint)x) >> (32 - n));
+ }
+
+ /*
+ * f1,f2,f3,f4,f5 are the basic RipeMD160 functions.
+ */
+
+ /*
+ * rounds 0-15
+ */
+ private int F1(int x, int y, int z)
+ {
+ return x ^ y ^ z;
+ }
+
+ /*
+ * rounds 16-31
+ */
+ private int F2(int x, int y, int z)
+ {
+ return (x & y) | (~ x & z);
+ }
+
+ /*
+ * rounds 32-47
+ */
+ private int F3(int x, int y, int z)
+ {
+ return (x | ~ y) ^ z;
+ }
+
+ /*
+ * rounds 48-63
+ */
+ private int F4(int x, int y, int z)
+ {
+ return (x & z) | (y & ~ z);
+ }
+
+ /*
+ * rounds 64-79
+ */
+ private int F5(int x, int y, int z)
+ {
+ return x ^ (y | ~z);
+ }
+
+ internal override void ProcessBlock()
+ {
+ int a, aa;
+ int b, bb;
+ int c, cc;
+ int d, dd;
+ int e, ee;
+ int t;
+
+ a = H0;
+ b = H1;
+ c = H2;
+ d = H3;
+ e = H4;
+ aa = H5;
+ bb = H6;
+ cc = H7;
+ dd = H8;
+ ee = H9;
+
+ //
+ // Rounds 1 - 16
+ //
+ // left
+ a = RL(a + F1(b, c, d) + X[0], 11) + e; c = RL(c, 10);
+ e = RL(e + F1(a, b, c) + X[1], 14) + d; b = RL(b, 10);
+ d = RL(d + F1(e, a, b) + X[2], 15) + c; a = RL(a, 10);
+ c = RL(c + F1(d, e, a) + X[3], 12) + b; e = RL(e, 10);
+ b = RL(b + F1(c, d, e) + X[4], 5) + a; d = RL(d, 10);
+ a = RL(a + F1(b, c, d) + X[5], 8) + e; c = RL(c, 10);
+ e = RL(e + F1(a, b, c) + X[6], 7) + d; b = RL(b, 10);
+ d = RL(d + F1(e, a, b) + X[7], 9) + c; a = RL(a, 10);
+ c = RL(c + F1(d, e, a) + X[8], 11) + b; e = RL(e, 10);
+ b = RL(b + F1(c, d, e) + X[9], 13) + a; d = RL(d, 10);
+ a = RL(a + F1(b, c, d) + X[10], 14) + e; c = RL(c, 10);
+ e = RL(e + F1(a, b, c) + X[11], 15) + d; b = RL(b, 10);
+ d = RL(d + F1(e, a, b) + X[12], 6) + c; a = RL(a, 10);
+ c = RL(c + F1(d, e, a) + X[13], 7) + b; e = RL(e, 10);
+ b = RL(b + F1(c, d, e) + X[14], 9) + a; d = RL(d, 10);
+ a = RL(a + F1(b, c, d) + X[15], 8) + e; c = RL(c, 10);
+
+ // right
+ aa = RL(aa + F5(bb, cc, dd) + X[5] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F5(aa, bb, cc) + X[14] + unchecked((int)0x50a28be6), 9) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F5(ee, aa, bb) + X[7] + unchecked((int)0x50a28be6), 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F5(dd, ee, aa) + X[0] + unchecked((int)0x50a28be6), 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F5(cc, dd, ee) + X[9] + unchecked((int)0x50a28be6), 13) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F5(bb, cc, dd) + X[2] + unchecked((int)0x50a28be6), 15) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F5(aa, bb, cc) + X[11] + unchecked((int)0x50a28be6), 15) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F5(ee, aa, bb) + X[4] + unchecked((int)0x50a28be6), 5) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F5(dd, ee, aa) + X[13] + unchecked((int)0x50a28be6), 7) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F5(cc, dd, ee) + X[6] + unchecked((int)0x50a28be6), 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F5(bb, cc, dd) + X[15] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F5(aa, bb, cc) + X[8] + unchecked((int)0x50a28be6), 11) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F5(ee, aa, bb) + X[1] + unchecked((int)0x50a28be6), 14) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F5(dd, ee, aa) + X[10] + unchecked((int)0x50a28be6), 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F5(cc, dd, ee) + X[3] + unchecked((int)0x50a28be6), 12) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F5(bb, cc, dd) + X[12] + unchecked((int)0x50a28be6), 6) + ee; cc = RL(cc, 10);
+
+ t = a; a = aa; aa = t;
+ //
+ // Rounds 16-31
+ //
+ // left
+ e = RL(e + F2(a, b, c) + X[7] + unchecked((int)0x5a827999), 7) + d; b = RL(b, 10);
+ d = RL(d + F2(e, a, b) + X[4] + unchecked((int)0x5a827999), 6) + c; a = RL(a, 10);
+ c = RL(c + F2(d, e, a) + X[13] + unchecked((int)0x5a827999), 8) + b; e = RL(e, 10);
+ b = RL(b + F2(c, d, e) + X[1] + unchecked((int)0x5a827999), 13) + a; d = RL(d, 10);
+ a = RL(a + F2(b, c, d) + X[10] + unchecked((int)0x5a827999), 11) + e; c = RL(c, 10);
+ e = RL(e + F2(a, b, c) + X[6] + unchecked((int)0x5a827999), 9) + d; b = RL(b, 10);
+ d = RL(d + F2(e, a, b) + X[15] + unchecked((int)0x5a827999), 7) + c; a = RL(a, 10);
+ c = RL(c + F2(d, e, a) + X[3] + unchecked((int)0x5a827999), 15) + b; e = RL(e, 10);
+ b = RL(b + F2(c, d, e) + X[12] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10);
+ a = RL(a + F2(b, c, d) + X[0] + unchecked((int)0x5a827999), 12) + e; c = RL(c, 10);
+ e = RL(e + F2(a, b, c) + X[9] + unchecked((int)0x5a827999), 15) + d; b = RL(b, 10);
+ d = RL(d + F2(e, a, b) + X[5] + unchecked((int)0x5a827999), 9) + c; a = RL(a, 10);
+ c = RL(c + F2(d, e, a) + X[2] + unchecked((int)0x5a827999), 11) + b; e = RL(e, 10);
+ b = RL(b + F2(c, d, e) + X[14] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10);
+ a = RL(a + F2(b, c, d) + X[11] + unchecked((int)0x5a827999), 13) + e; c = RL(c, 10);
+ e = RL(e + F2(a, b, c) + X[8] + unchecked((int)0x5a827999), 12) + d; b = RL(b, 10);
+
+ // right
+ ee = RL(ee + F4(aa, bb, cc) + X[6] + unchecked((int)0x5c4dd124), 9) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F4(ee, aa, bb) + X[11] + unchecked((int)0x5c4dd124), 13) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F4(dd, ee, aa) + X[3] + unchecked((int)0x5c4dd124), 15) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F4(cc, dd, ee) + X[7] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F4(bb, cc, dd) + X[0] + unchecked((int)0x5c4dd124), 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F4(aa, bb, cc) + X[13] + unchecked((int)0x5c4dd124), 8) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F4(ee, aa, bb) + X[5] + unchecked((int)0x5c4dd124), 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F4(dd, ee, aa) + X[10] + unchecked((int)0x5c4dd124), 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F4(cc, dd, ee) + X[14] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F4(bb, cc, dd) + X[15] + unchecked((int)0x5c4dd124), 7) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F4(aa, bb, cc) + X[8] + unchecked((int)0x5c4dd124), 12) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F4(ee, aa, bb) + X[12] + unchecked((int)0x5c4dd124), 7) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F4(dd, ee, aa) + X[4] + unchecked((int)0x5c4dd124), 6) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F4(cc, dd, ee) + X[9] + unchecked((int)0x5c4dd124), 15) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F4(bb, cc, dd) + X[1] + unchecked((int)0x5c4dd124), 13) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F4(aa, bb, cc) + X[2] + unchecked((int)0x5c4dd124), 11) + dd; bb = RL(bb, 10);
+
+ t = b; b = bb; bb = t;
+
+ //
+ // Rounds 32-47
+ //
+ // left
+ d = RL(d + F3(e, a, b) + X[3] + unchecked((int)0x6ed9eba1), 11) + c; a = RL(a, 10);
+ c = RL(c + F3(d, e, a) + X[10] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10);
+ b = RL(b + F3(c, d, e) + X[14] + unchecked((int)0x6ed9eba1), 6) + a; d = RL(d, 10);
+ a = RL(a + F3(b, c, d) + X[4] + unchecked((int)0x6ed9eba1), 7) + e; c = RL(c, 10);
+ e = RL(e + F3(a, b, c) + X[9] + unchecked((int)0x6ed9eba1), 14) + d; b = RL(b, 10);
+ d = RL(d + F3(e, a, b) + X[15] + unchecked((int)0x6ed9eba1), 9) + c; a = RL(a, 10);
+ c = RL(c + F3(d, e, a) + X[8] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10);
+ b = RL(b + F3(c, d, e) + X[1] + unchecked((int)0x6ed9eba1), 15) + a; d = RL(d, 10);
+ a = RL(a + F3(b, c, d) + X[2] + unchecked((int)0x6ed9eba1), 14) + e; c = RL(c, 10);
+ e = RL(e + F3(a, b, c) + X[7] + unchecked((int)0x6ed9eba1), 8) + d; b = RL(b, 10);
+ d = RL(d + F3(e, a, b) + X[0] + unchecked((int)0x6ed9eba1), 13) + c; a = RL(a, 10);
+ c = RL(c + F3(d, e, a) + X[6] + unchecked((int)0x6ed9eba1), 6) + b; e = RL(e, 10);
+ b = RL(b + F3(c, d, e) + X[13] + unchecked((int)0x6ed9eba1), 5) + a; d = RL(d, 10);
+ a = RL(a + F3(b, c, d) + X[11] + unchecked((int)0x6ed9eba1), 12) + e; c = RL(c, 10);
+ e = RL(e + F3(a, b, c) + X[5] + unchecked((int)0x6ed9eba1), 7) + d; b = RL(b, 10);
+ d = RL(d + F3(e, a, b) + X[12] + unchecked((int)0x6ed9eba1), 5) + c; a = RL(a, 10);
+
+ // right
+ dd = RL(dd + F3(ee, aa, bb) + X[15] + unchecked((int)0x6d703ef3), 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F3(dd, ee, aa) + X[5] + unchecked((int)0x6d703ef3), 7) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F3(cc, dd, ee) + X[1] + unchecked((int)0x6d703ef3), 15) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F3(bb, cc, dd) + X[3] + unchecked((int)0x6d703ef3), 11) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F3(aa, bb, cc) + X[7] + unchecked((int)0x6d703ef3), 8) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F3(ee, aa, bb) + X[14] + unchecked((int)0x6d703ef3), 6) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F3(dd, ee, aa) + X[6] + unchecked((int)0x6d703ef3), 6) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F3(cc, dd, ee) + X[9] + unchecked((int)0x6d703ef3), 14) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F3(bb, cc, dd) + X[11] + unchecked((int)0x6d703ef3), 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F3(aa, bb, cc) + X[8] + unchecked((int)0x6d703ef3), 13) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F3(ee, aa, bb) + X[12] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F3(dd, ee, aa) + X[2] + unchecked((int)0x6d703ef3), 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F3(cc, dd, ee) + X[10] + unchecked((int)0x6d703ef3), 13) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F3(bb, cc, dd) + X[0] + unchecked((int)0x6d703ef3), 13) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F3(aa, bb, cc) + X[4] + unchecked((int)0x6d703ef3), 7) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F3(ee, aa, bb) + X[13] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10);
+
+ t = c; c = cc; cc = t;
+
+ //
+ // Rounds 48-63
+ //
+ // left
+ c = RL(c + F4(d, e, a) + X[1] + unchecked((int)0x8f1bbcdc), 11) + b; e = RL(e, 10);
+ b = RL(b + F4(c, d, e) + X[9] + unchecked((int)0x8f1bbcdc), 12) + a; d = RL(d, 10);
+ a = RL(a + F4(b, c, d) + X[11] + unchecked((int)0x8f1bbcdc), 14) + e; c = RL(c, 10);
+ e = RL(e + F4(a, b, c) + X[10] + unchecked((int)0x8f1bbcdc), 15) + d; b = RL(b, 10);
+ d = RL(d + F4(e, a, b) + X[0] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10);
+ c = RL(c + F4(d, e, a) + X[8] + unchecked((int)0x8f1bbcdc), 15) + b; e = RL(e, 10);
+ b = RL(b + F4(c, d, e) + X[12] + unchecked((int)0x8f1bbcdc), 9) + a; d = RL(d, 10);
+ a = RL(a + F4(b, c, d) + X[4] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10);
+ e = RL(e + F4(a, b, c) + X[13] + unchecked((int)0x8f1bbcdc), 9) + d; b = RL(b, 10);
+ d = RL(d + F4(e, a, b) + X[3] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10);
+ c = RL(c + F4(d, e, a) + X[7] + unchecked((int)0x8f1bbcdc), 5) + b; e = RL(e, 10);
+ b = RL(b + F4(c, d, e) + X[15] + unchecked((int)0x8f1bbcdc), 6) + a; d = RL(d, 10);
+ a = RL(a + F4(b, c, d) + X[14] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10);
+ e = RL(e + F4(a, b, c) + X[5] + unchecked((int)0x8f1bbcdc), 6) + d; b = RL(b, 10);
+ d = RL(d + F4(e, a, b) + X[6] + unchecked((int)0x8f1bbcdc), 5) + c; a = RL(a, 10);
+ c = RL(c + F4(d, e, a) + X[2] + unchecked((int)0x8f1bbcdc), 12) + b; e = RL(e, 10);
+
+ // right
+ cc = RL(cc + F2(dd, ee, aa) + X[8] + unchecked((int)0x7a6d76e9), 15) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F2(cc, dd, ee) + X[6] + unchecked((int)0x7a6d76e9), 5) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F2(bb, cc, dd) + X[4] + unchecked((int)0x7a6d76e9), 8) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F2(aa, bb, cc) + X[1] + unchecked((int)0x7a6d76e9), 11) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F2(ee, aa, bb) + X[3] + unchecked((int)0x7a6d76e9), 14) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F2(dd, ee, aa) + X[11] + unchecked((int)0x7a6d76e9), 14) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F2(cc, dd, ee) + X[15] + unchecked((int)0x7a6d76e9), 6) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F2(bb, cc, dd) + X[0] + unchecked((int)0x7a6d76e9), 14) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F2(aa, bb, cc) + X[5] + unchecked((int)0x7a6d76e9), 6) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F2(ee, aa, bb) + X[12] + unchecked((int)0x7a6d76e9), 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F2(dd, ee, aa) + X[2] + unchecked((int)0x7a6d76e9), 12) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F2(cc, dd, ee) + X[13] + unchecked((int)0x7a6d76e9), 9) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F2(bb, cc, dd) + X[9] + unchecked((int)0x7a6d76e9), 12) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F2(aa, bb, cc) + X[7] + unchecked((int)0x7a6d76e9), 5) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F2(ee, aa, bb) + X[10] + unchecked((int)0x7a6d76e9), 15) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F2(dd, ee, aa) + X[14] + unchecked((int)0x7a6d76e9), 8) + bb; ee = RL(ee, 10);
+
+ t = d; d = dd; dd = t;
+
+ //
+ // Rounds 64-79
+ //
+ // left
+ b = RL(b + F5(c, d, e) + X[4] + unchecked((int)0xa953fd4e), 9) + a; d = RL(d, 10);
+ a = RL(a + F5(b, c, d) + X[0] + unchecked((int)0xa953fd4e), 15) + e; c = RL(c, 10);
+ e = RL(e + F5(a, b, c) + X[5] + unchecked((int)0xa953fd4e), 5) + d; b = RL(b, 10);
+ d = RL(d + F5(e, a, b) + X[9] + unchecked((int)0xa953fd4e), 11) + c; a = RL(a, 10);
+ c = RL(c + F5(d, e, a) + X[7] + unchecked((int)0xa953fd4e), 6) + b; e = RL(e, 10);
+ b = RL(b + F5(c, d, e) + X[12] + unchecked((int)0xa953fd4e), 8) + a; d = RL(d, 10);
+ a = RL(a + F5(b, c, d) + X[2] + unchecked((int)0xa953fd4e), 13) + e; c = RL(c, 10);
+ e = RL(e + F5(a, b, c) + X[10] + unchecked((int)0xa953fd4e), 12) + d; b = RL(b, 10);
+ d = RL(d + F5(e, a, b) + X[14] + unchecked((int)0xa953fd4e), 5) + c; a = RL(a, 10);
+ c = RL(c + F5(d, e, a) + X[1] + unchecked((int)0xa953fd4e), 12) + b; e = RL(e, 10);
+ b = RL(b + F5(c, d, e) + X[3] + unchecked((int)0xa953fd4e), 13) + a; d = RL(d, 10);
+ a = RL(a + F5(b, c, d) + X[8] + unchecked((int)0xa953fd4e), 14) + e; c = RL(c, 10);
+ e = RL(e + F5(a, b, c) + X[11] + unchecked((int)0xa953fd4e), 11) + d; b = RL(b, 10);
+ d = RL(d + F5(e, a, b) + X[6] + unchecked((int)0xa953fd4e), 8) + c; a = RL(a, 10);
+ c = RL(c + F5(d, e, a) + X[15] + unchecked((int)0xa953fd4e), 5) + b; e = RL(e, 10);
+ b = RL(b + F5(c, d, e) + X[13] + unchecked((int)0xa953fd4e), 6) + a; d = RL(d, 10);
+
+ // right
+ bb = RL(bb + F1(cc, dd, ee) + X[12], 8) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F1(bb, cc, dd) + X[15], 5) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F1(aa, bb, cc) + X[10], 12) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F1(ee, aa, bb) + X[4], 9) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F1(dd, ee, aa) + X[1], 12) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F1(cc, dd, ee) + X[5], 5) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F1(bb, cc, dd) + X[8], 14) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F1(aa, bb, cc) + X[7], 6) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F1(ee, aa, bb) + X[6], 8) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F1(dd, ee, aa) + X[2], 13) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F1(cc, dd, ee) + X[13], 6) + aa; dd = RL(dd, 10);
+ aa = RL(aa + F1(bb, cc, dd) + X[14], 5) + ee; cc = RL(cc, 10);
+ ee = RL(ee + F1(aa, bb, cc) + X[0], 15) + dd; bb = RL(bb, 10);
+ dd = RL(dd + F1(ee, aa, bb) + X[3], 13) + cc; aa = RL(aa, 10);
+ cc = RL(cc + F1(dd, ee, aa) + X[9], 11) + bb; ee = RL(ee, 10);
+ bb = RL(bb + F1(cc, dd, ee) + X[11], 11) + aa; dd = RL(dd, 10);
+
+ //
+ // do (e, ee) swap as part of assignment.
+ //
+
+ H0 += a;
+ H1 += b;
+ H2 += c;
+ H3 += d;
+ H4 += ee;
+ H5 += aa;
+ H6 += bb;
+ H7 += cc;
+ H8 += dd;
+ H9 += e;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.Length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/digests/Sha1Digest.cs b/Crypto/src/crypto/digests/Sha1Digest.cs
new file mode 100644
index 000000000..9d8c1a4cf
--- /dev/null
+++ b/Crypto/src/crypto/digests/Sha1Digest.cs
@@ -0,0 +1,263 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+
+ /**
+ * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349.
+ *
+ * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5
+ * is the "endienness" of the word processing!
+ */
+ public class Sha1Digest
+ : GeneralDigest
+ {
+ private const int DigestLength = 20;
+
+ private uint H1, H2, H3, H4, H5;
+
+ private uint[] X = new uint[80];
+ private int xOff;
+
+ public Sha1Digest()
+ {
+ Reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public Sha1Digest(Sha1Digest t)
+ : base(t)
+ {
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "SHA-1"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ internal override void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ X[xOff] = Pack.BE_To_UInt32(input, inOff);
+
+ if (++xOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ internal override void ProcessLength(long bitLength)
+ {
+ if (xOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ X[14] = (uint)((ulong)bitLength >> 32);
+ X[15] = (uint)((ulong)bitLength);
+ }
+
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ Pack.UInt32_To_BE(H1, output, outOff);
+ Pack.UInt32_To_BE(H2, output, outOff + 4);
+ Pack.UInt32_To_BE(H3, output, outOff + 8);
+ Pack.UInt32_To_BE(H4, output, outOff + 12);
+ Pack.UInt32_To_BE(H5, output, outOff + 16);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public override void Reset()
+ {
+ base.Reset();
+
+ H1 = 0x67452301;
+ H2 = 0xefcdab89;
+ H3 = 0x98badcfe;
+ H4 = 0x10325476;
+ H5 = 0xc3d2e1f0;
+
+ xOff = 0;
+ Array.Clear(X, 0, X.Length);
+ }
+
+ //
+ // Additive constants
+ //
+ private const uint Y1 = 0x5a827999;
+ private const uint Y2 = 0x6ed9eba1;
+ private const uint Y3 = 0x8f1bbcdc;
+ private const uint Y4 = 0xca62c1d6;
+
+ private static uint F(uint u, uint v, uint w)
+ {
+ return (u & v) | (~u & w);
+ }
+
+ private static uint H(uint u, uint v, uint w)
+ {
+ return u ^ v ^ w;
+ }
+
+ private static uint G(uint u, uint v, uint w)
+ {
+ return (u & v) | (u & w) | (v & w);
+ }
+
+ internal override void ProcessBlock()
+ {
+ //
+ // expand 16 word block into 80 word block.
+ //
+ for (int i = 16; i < 80; i++)
+ {
+ uint t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16];
+ X[i] = t << 1 | t >> 31;
+ }
+
+ //
+ // set up working variables.
+ //
+ uint A = H1;
+ uint B = H2;
+ uint C = H3;
+ uint D = H4;
+ uint E = H5;
+
+ //
+ // round 1
+ //
+ int idx = 0;
+
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | (A >> 27)) + F(B, C, D) + X[idx++] + Y1;
+ B = B << 30 | (B >> 2);
+
+ D += (E << 5 | (E >> 27)) + F(A, B, C) + X[idx++] + Y1;
+ A = A << 30 | (A >> 2);
+
+ C += (D << 5 | (D >> 27)) + F(E, A, B) + X[idx++] + Y1;
+ E = E << 30 | (E >> 2);
+
+ B += (C << 5 | (C >> 27)) + F(D, E, A) + X[idx++] + Y1;
+ D = D << 30 | (D >> 2);
+
+ A += (B << 5 | (B >> 27)) + F(C, D, E) + X[idx++] + Y1;
+ C = C << 30 | (C >> 2);
+ }
+
+ //
+ // round 2
+ //
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | (A >> 27)) + H(B, C, D) + X[idx++] + Y2;
+ B = B << 30 | (B >> 2);
+
+ D += (E << 5 | (E >> 27)) + H(A, B, C) + X[idx++] + Y2;
+ A = A << 30 | (A >> 2);
+
+ C += (D << 5 | (D >> 27)) + H(E, A, B) + X[idx++] + Y2;
+ E = E << 30 | (E >> 2);
+
+ B += (C << 5 | (C >> 27)) + H(D, E, A) + X[idx++] + Y2;
+ D = D << 30 | (D >> 2);
+
+ A += (B << 5 | (B >> 27)) + H(C, D, E) + X[idx++] + Y2;
+ C = C << 30 | (C >> 2);
+ }
+
+ //
+ // round 3
+ //
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | (A >> 27)) + G(B, C, D) + X[idx++] + Y3;
+ B = B << 30 | (B >> 2);
+
+ D += (E << 5 | (E >> 27)) + G(A, B, C) + X[idx++] + Y3;
+ A = A << 30 | (A >> 2);
+
+ C += (D << 5 | (D >> 27)) + G(E, A, B) + X[idx++] + Y3;
+ E = E << 30 | (E >> 2);
+
+ B += (C << 5 | (C >> 27)) + G(D, E, A) + X[idx++] + Y3;
+ D = D << 30 | (D >> 2);
+
+ A += (B << 5 | (B >> 27)) + G(C, D, E) + X[idx++] + Y3;
+ C = C << 30 | (C >> 2);
+ }
+
+ //
+ // round 4
+ //
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | (A >> 27)) + H(B, C, D) + X[idx++] + Y4;
+ B = B << 30 | (B >> 2);
+
+ D += (E << 5 | (E >> 27)) + H(A, B, C) + X[idx++] + Y4;
+ A = A << 30 | (A >> 2);
+
+ C += (D << 5 | (D >> 27)) + H(E, A, B) + X[idx++] + Y4;
+ E = E << 30 | (E >> 2);
+
+ B += (C << 5 | (C >> 27)) + H(D, E, A) + X[idx++] + Y4;
+ D = D << 30 | (D >> 2);
+
+ A += (B << 5 | (B >> 27)) + H(C, D, E) + X[idx++] + Y4;
+ C = C << 30 | (C >> 2);
+ }
+
+ H1 += A;
+ H2 += B;
+ H3 += C;
+ H4 += D;
+ H5 += E;
+
+ //
+ // reset start of the buffer.
+ //
+ xOff = 0;
+ Array.Clear(X, 0, 16);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/digests/Sha224Digest.cs b/Crypto/src/crypto/digests/Sha224Digest.cs
new file mode 100644
index 000000000..66ecd4ecd
--- /dev/null
+++ b/Crypto/src/crypto/digests/Sha224Digest.cs
@@ -0,0 +1,268 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * SHA-224 as described in RFC 3874
+ * <pre>
+ * block word digest
+ * SHA-1 512 32 160
+ * SHA-224 512 32 224
+ * SHA-256 512 32 256
+ * SHA-384 1024 64 384
+ * SHA-512 1024 64 512
+ * </pre>
+ */
+ public class Sha224Digest
+ : GeneralDigest
+ {
+ private const int DigestLength = 28;
+
+ private uint H1, H2, H3, H4, H5, H6, H7, H8;
+
+ private uint[] X = new uint[64];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public Sha224Digest()
+ {
+ Reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public Sha224Digest(
+ Sha224Digest t)
+ : base(t)
+ {
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+ H8 = t.H8;
+
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "SHA-224"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ internal override void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ X[xOff] = Pack.BE_To_UInt32(input, inOff);
+
+ if (++xOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ internal override void ProcessLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ X[14] = (uint)((ulong)bitLength >> 32);
+ X[15] = (uint)((ulong)bitLength);
+ }
+
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ Pack.UInt32_To_BE(H1, output, outOff);
+ Pack.UInt32_To_BE(H2, output, outOff + 4);
+ Pack.UInt32_To_BE(H3, output, outOff + 8);
+ Pack.UInt32_To_BE(H4, output, outOff + 12);
+ Pack.UInt32_To_BE(H5, output, outOff + 16);
+ Pack.UInt32_To_BE(H6, output, outOff + 20);
+ Pack.UInt32_To_BE(H7, output, outOff + 24);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public override void Reset()
+ {
+ base.Reset();
+
+ /* SHA-224 initial hash value
+ */
+ H1 = 0xc1059ed8;
+ H2 = 0x367cd507;
+ H3 = 0x3070dd17;
+ H4 = 0xf70e5939;
+ H5 = 0xffc00b31;
+ H6 = 0x68581511;
+ H7 = 0x64f98fa7;
+ H8 = 0xbefa4fa4;
+
+ xOff = 0;
+ Array.Clear(X, 0, X.Length);
+ }
+
+ internal override void ProcessBlock()
+ {
+ //
+ // expand 16 word block into 64 word blocks.
+ //
+ for (int ti = 16; ti <= 63; ti++)
+ {
+ X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16];
+ }
+
+ //
+ // set up working variables.
+ //
+ uint a = H1;
+ uint b = H2;
+ uint c = H3;
+ uint d = H4;
+ uint e = H5;
+ uint f = H6;
+ uint g = H7;
+ uint h = H8;
+
+ int t = 0;
+ for(int i = 0; i < 8; i ++)
+ {
+ // t = 8 * i
+ h += Sum1(e) + Ch(e, f, g) + K[t] + X[t];
+ d += h;
+ h += Sum0(a) + Maj(a, b, c);
+ ++t;
+
+ // t = 8 * i + 1
+ g += Sum1(d) + Ch(d, e, f) + K[t] + X[t];
+ c += g;
+ g += Sum0(h) + Maj(h, a, b);
+ ++t;
+
+ // t = 8 * i + 2
+ f += Sum1(c) + Ch(c, d, e) + K[t] + X[t];
+ b += f;
+ f += Sum0(g) + Maj(g, h, a);
+ ++t;
+
+ // t = 8 * i + 3
+ e += Sum1(b) + Ch(b, c, d) + K[t] + X[t];
+ a += e;
+ e += Sum0(f) + Maj(f, g, h);
+ ++t;
+
+ // t = 8 * i + 4
+ d += Sum1(a) + Ch(a, b, c) + K[t] + X[t];
+ h += d;
+ d += Sum0(e) + Maj(e, f, g);
+ ++t;
+
+ // t = 8 * i + 5
+ c += Sum1(h) + Ch(h, a, b) + K[t] + X[t];
+ g += c;
+ c += Sum0(d) + Maj(d, e, f);
+ ++t;
+
+ // t = 8 * i + 6
+ b += Sum1(g) + Ch(g, h, a) + K[t] + X[t];
+ f += b;
+ b += Sum0(c) + Maj(c, d, e);
+ ++t;
+
+ // t = 8 * i + 7
+ a += Sum1(f) + Ch(f, g, h) + K[t] + X[t];
+ e += a;
+ a += Sum0(b) + Maj(b, c, d);
+ ++t;
+ }
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+ H5 += e;
+ H6 += f;
+ H7 += g;
+ H8 += h;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ Array.Clear(X, 0, 16);
+ }
+
+ /* SHA-224 functions */
+ private static uint Ch(uint x, uint y, uint z)
+ {
+ return (x & y) ^ (~x & z);
+ }
+
+ private static uint Maj(uint x, uint y, uint z)
+ {
+ return (x & y) ^ (x & z) ^ (y & z);
+ }
+
+ private static uint Sum0(uint x)
+ {
+ return ((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10));
+ }
+
+ private static uint Sum1(uint x)
+ {
+ return ((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7));
+ }
+
+ private static uint Theta0(uint x)
+ {
+ return ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3);
+ }
+
+ private static uint Theta1(uint x)
+ {
+ return ((x >> 17) | (x << 15)) ^ ((x >> 19) | (x << 13)) ^ (x >> 10);
+ }
+
+ /* SHA-224 Constants
+ * (represent the first 32 bits of the fractional parts of the
+ * cube roots of the first sixty-four prime numbers)
+ */
+ internal static readonly uint[] K = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+ }
+}
diff --git a/Crypto/src/crypto/digests/Sha256Digest.cs b/Crypto/src/crypto/digests/Sha256Digest.cs
new file mode 100644
index 000000000..1c00ab71f
--- /dev/null
+++ b/Crypto/src/crypto/digests/Sha256Digest.cs
@@ -0,0 +1,309 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * Draft FIPS 180-2 implementation of SHA-256. <b>Note:</b> As this is
+ * based on a draft this implementation is subject to change.
+ *
+ * <pre>
+ * block word digest
+ * SHA-1 512 32 160
+ * SHA-256 512 32 256
+ * SHA-384 1024 64 384
+ * SHA-512 1024 64 512
+ * </pre>
+ */
+ public class Sha256Digest
+ : GeneralDigest
+ {
+ private const int DigestLength = 32;
+
+ private uint H1, H2, H3, H4, H5, H6, H7, H8;
+ private uint[] X = new uint[64];
+ private int xOff;
+
+ public Sha256Digest()
+ {
+ initHs();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public Sha256Digest(Sha256Digest t) : base(t)
+ {
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+ H6 = t.H6;
+ H7 = t.H7;
+ H8 = t.H8;
+
+ Array.Copy(t.X, 0, X, 0, t.X.Length);
+ xOff = t.xOff;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "SHA-256"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ internal override void ProcessWord(
+ byte[] input,
+ int inOff)
+ {
+ X[xOff] = Pack.BE_To_UInt32(input, inOff);
+
+ if (++xOff == 16)
+ {
+ ProcessBlock();
+ }
+ }
+
+ internal override void ProcessLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ ProcessBlock();
+ }
+
+ X[14] = (uint)((ulong)bitLength >> 32);
+ X[15] = (uint)((ulong)bitLength);
+ }
+
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ Pack.UInt32_To_BE((uint)H1, output, outOff);
+ Pack.UInt32_To_BE((uint)H2, output, outOff + 4);
+ Pack.UInt32_To_BE((uint)H3, output, outOff + 8);
+ Pack.UInt32_To_BE((uint)H4, output, outOff + 12);
+ Pack.UInt32_To_BE((uint)H5, output, outOff + 16);
+ Pack.UInt32_To_BE((uint)H6, output, outOff + 20);
+ Pack.UInt32_To_BE((uint)H7, output, outOff + 24);
+ Pack.UInt32_To_BE((uint)H8, output, outOff + 28);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public override void Reset()
+ {
+ base.Reset();
+
+ initHs();
+
+ xOff = 0;
+ Array.Clear(X, 0, X.Length);
+ }
+
+ private void initHs()
+ {
+ /* SHA-256 initial hash value
+ * The first 32 bits of the fractional parts of the square roots
+ * of the first eight prime numbers
+ */
+ H1 = 0x6a09e667;
+ H2 = 0xbb67ae85;
+ H3 = 0x3c6ef372;
+ H4 = 0xa54ff53a;
+ H5 = 0x510e527f;
+ H6 = 0x9b05688c;
+ H7 = 0x1f83d9ab;
+ H8 = 0x5be0cd19;
+ }
+
+ internal override void ProcessBlock()
+ {
+ //
+ // expand 16 word block into 64 word blocks.
+ //
+ for (int ti = 16; ti <= 63; ti++)
+ {
+ X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16];
+ }
+
+ //
+ // set up working variables.
+ //
+ uint a = H1;
+ uint b = H2;
+ uint c = H3;
+ uint d = H4;
+ uint e = H5;
+ uint f = H6;
+ uint g = H7;
+ uint h = H8;
+
+ int t = 0;
+ for(int i = 0; i < 8; ++i)
+ {
+ // t = 8 * i
+ h += Sum1Ch(e, f, g) + K[t] + X[t];
+ d += h;
+ h += Sum0Maj(a, b, c);
+ ++t;
+
+ // t = 8 * i + 1
+ g += Sum1Ch(d, e, f) + K[t] + X[t];
+ c += g;
+ g += Sum0Maj(h, a, b);
+ ++t;
+
+ // t = 8 * i + 2
+ f += Sum1Ch(c, d, e) + K[t] + X[t];
+ b += f;
+ f += Sum0Maj(g, h, a);
+ ++t;
+
+ // t = 8 * i + 3
+ e += Sum1Ch(b, c, d) + K[t] + X[t];
+ a += e;
+ e += Sum0Maj(f, g, h);
+ ++t;
+
+ // t = 8 * i + 4
+ d += Sum1Ch(a, b, c) + K[t] + X[t];
+ h += d;
+ d += Sum0Maj(e, f, g);
+ ++t;
+
+ // t = 8 * i + 5
+ c += Sum1Ch(h, a, b) + K[t] + X[t];
+ g += c;
+ c += Sum0Maj(d, e, f);
+ ++t;
+
+ // t = 8 * i + 6
+ b += Sum1Ch(g, h, a) + K[t] + X[t];
+ f += b;
+ b += Sum0Maj(c, d, e);
+ ++t;
+
+ // t = 8 * i + 7
+ a += Sum1Ch(f, g, h) + K[t] + X[t];
+ e += a;
+ a += Sum0Maj(b, c, d);
+ ++t;
+ }
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+ H5 += e;
+ H6 += f;
+ H7 += g;
+ H8 += h;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ Array.Clear(X, 0, 16);
+ }
+
+ private static uint Sum1Ch(
+ uint x,
+ uint y,
+ uint z)
+ {
+// return Sum1(x) + Ch(x, y, z);
+ return (((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7)))
+ + ((x & y) ^ ((~x) & z));
+ }
+
+ private static uint Sum0Maj(
+ uint x,
+ uint y,
+ uint z)
+ {
+// return Sum0(x) + Maj(x, y, z);
+ return (((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10)))
+ + ((x & y) ^ (x & z) ^ (y & z));
+ }
+
+// /* SHA-256 functions */
+// private static uint Ch(
+// uint x,
+// uint y,
+// uint z)
+// {
+// return ((x & y) ^ ((~x) & z));
+// }
+//
+// private static uint Maj(
+// uint x,
+// uint y,
+// uint z)
+// {
+// return ((x & y) ^ (x & z) ^ (y & z));
+// }
+//
+// private static uint Sum0(
+// uint x)
+// {
+// return ((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10));
+// }
+//
+// private static uint Sum1(
+// uint x)
+// {
+// return ((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7));
+// }
+
+ private static uint Theta0(
+ uint x)
+ {
+ return ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3);
+ }
+
+ private static uint Theta1(
+ uint x)
+ {
+ return ((x >> 17) | (x << 15)) ^ ((x >> 19) | (x << 13)) ^ (x >> 10);
+ }
+
+ /* SHA-256 Constants
+ * (represent the first 32 bits of the fractional parts of the
+ * cube roots of the first sixty-four prime numbers)
+ */
+ private static readonly uint[] K = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+ }
+}
diff --git a/Crypto/src/crypto/digests/Sha384Digest.cs b/Crypto/src/crypto/digests/Sha384Digest.cs
new file mode 100644
index 000000000..f1372d0a9
--- /dev/null
+++ b/Crypto/src/crypto/digests/Sha384Digest.cs
@@ -0,0 +1,87 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * Draft FIPS 180-2 implementation of SHA-384. <b>Note:</b> As this is
+ * based on a draft this implementation is subject to change.
+ *
+ * <pre>
+ * block word digest
+ * SHA-1 512 32 160
+ * SHA-256 512 32 256
+ * SHA-384 1024 64 384
+ * SHA-512 1024 64 512
+ * </pre>
+ */
+ public class Sha384Digest
+ : LongDigest
+ {
+ private const int DigestLength = 48;
+
+ public Sha384Digest()
+ {
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public Sha384Digest(
+ Sha384Digest t)
+ : base(t)
+ {
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "SHA-384"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ Pack.UInt64_To_BE(H1, output, outOff);
+ Pack.UInt64_To_BE(H2, output, outOff + 8);
+ Pack.UInt64_To_BE(H3, output, outOff + 16);
+ Pack.UInt64_To_BE(H4, output, outOff + 24);
+ Pack.UInt64_To_BE(H5, output, outOff + 32);
+ Pack.UInt64_To_BE(H6, output, outOff + 40);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public override void Reset()
+ {
+ base.Reset();
+
+ /* SHA-384 initial hash value
+ * The first 64 bits of the fractional parts of the square roots
+ * of the 9th through 16th prime numbers
+ */
+ H1 = 0xcbbb9d5dc1059ed8;
+ H2 = 0x629a292a367cd507;
+ H3 = 0x9159015a3070dd17;
+ H4 = 0x152fecd8f70e5939;
+ H5 = 0x67332667ffc00b31;
+ H6 = 0x8eb44a8768581511;
+ H7 = 0xdb0c2e0d64f98fa7;
+ H8 = 0x47b5481dbefa4fa4;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/digests/Sha512Digest.cs b/Crypto/src/crypto/digests/Sha512Digest.cs
new file mode 100644
index 000000000..ed1a50819
--- /dev/null
+++ b/Crypto/src/crypto/digests/Sha512Digest.cs
@@ -0,0 +1,90 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * Draft FIPS 180-2 implementation of SHA-512. <b>Note:</b> As this is
+ * based on a draft this implementation is subject to change.
+ *
+ * <pre>
+ * block word digest
+ * SHA-1 512 32 160
+ * SHA-256 512 32 256
+ * SHA-384 1024 64 384
+ * SHA-512 1024 64 512
+ * </pre>
+ */
+ public class Sha512Digest
+ : LongDigest
+ {
+ private const int DigestLength = 64;
+
+ public Sha512Digest()
+ {
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public Sha512Digest(
+ Sha512Digest t)
+ : base(t)
+ {
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "SHA-512"; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ Pack.UInt64_To_BE(H1, output, outOff);
+ Pack.UInt64_To_BE(H2, output, outOff + 8);
+ Pack.UInt64_To_BE(H3, output, outOff + 16);
+ Pack.UInt64_To_BE(H4, output, outOff + 24);
+ Pack.UInt64_To_BE(H5, output, outOff + 32);
+ Pack.UInt64_To_BE(H6, output, outOff + 40);
+ Pack.UInt64_To_BE(H7, output, outOff + 48);
+ Pack.UInt64_To_BE(H8, output, outOff + 56);
+
+ Reset();
+
+ return DigestLength;
+
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public override void Reset()
+ {
+ base.Reset();
+
+ /* SHA-512 initial hash value
+ * The first 64 bits of the fractional parts of the square roots
+ * of the first eight prime numbers
+ */
+ H1 = 0x6a09e667f3bcc908;
+ H2 = 0xbb67ae8584caa73b;
+ H3 = 0x3c6ef372fe94f82b;
+ H4 = 0xa54ff53a5f1d36f1;
+ H5 = 0x510e527fade682d1;
+ H6 = 0x9b05688c2b3e6c1f;
+ H7 = 0x1f83d9abfb41bd6b;
+ H8 = 0x5be0cd19137e2179;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/digests/ShortenedDigest.cs b/Crypto/src/crypto/digests/ShortenedDigest.cs
new file mode 100644
index 000000000..9e4d99e7b
--- /dev/null
+++ b/Crypto/src/crypto/digests/ShortenedDigest.cs
@@ -0,0 +1,82 @@
+using System;
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * Wrapper class that reduces the output length of a particular digest to
+ * only the first n bytes of the digest function.
+ */
+ public class ShortenedDigest
+ : IDigest
+ {
+ private IDigest baseDigest;
+ private int length;
+
+ /**
+ * Base constructor.
+ *
+ * @param baseDigest underlying digest to use.
+ * @param length length in bytes of the output of doFinal.
+ * @exception ArgumentException if baseDigest is null, or length is greater than baseDigest.GetDigestSize().
+ */
+ public ShortenedDigest(
+ IDigest baseDigest,
+ int length)
+ {
+ if (baseDigest == null)
+ {
+ throw new ArgumentNullException("baseDigest");
+ }
+
+ if (length > baseDigest.GetDigestSize())
+ {
+ throw new ArgumentException("baseDigest output not large enough to support length");
+ }
+
+ this.baseDigest = baseDigest;
+ this.length = length;
+ }
+
+ public string AlgorithmName
+ {
+ get { return baseDigest.AlgorithmName + "(" + length * 8 + ")"; }
+ }
+
+ public int GetDigestSize()
+ {
+ return length;
+ }
+
+ public void Update(byte input)
+ {
+ baseDigest.Update(input);
+ }
+
+ public void BlockUpdate(byte[] input, int inOff, int length)
+ {
+ baseDigest.BlockUpdate(input, inOff, length);
+ }
+
+ public int DoFinal(byte[] output, int outOff)
+ {
+ byte[] tmp = new byte[baseDigest.GetDigestSize()];
+
+ baseDigest.DoFinal(tmp, 0);
+
+ Array.Copy(tmp, 0, output, outOff, length);
+
+ return length;
+ }
+
+ public void Reset()
+ {
+ baseDigest.Reset();
+ }
+
+ public int GetByteLength()
+ {
+ return baseDigest.GetByteLength();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/digests/TigerDigest.cs b/Crypto/src/crypto/digests/TigerDigest.cs
new file mode 100644
index 000000000..b8c9a7664
--- /dev/null
+++ b/Crypto/src/crypto/digests/TigerDigest.cs
@@ -0,0 +1,868 @@
+using System;
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * implementation of Tiger based on:
+ * <a href="http://www.cs.technion.ac.il/~biham/Reports/Tiger">
+ * http://www.cs.technion.ac.il/~biham/Reports/Tiger</a>
+ */
+ public class TigerDigest
+ : IDigest
+ {
+ private const int MyByteLength = 64;
+
+ /*
+ * S-Boxes.
+ */
+ private static readonly long[] t1 = {
+ unchecked((long) 0x02AAB17CF7E90C5EL) /* 0 */, unchecked((long) 0xAC424B03E243A8ECL) /* 1 */,
+ unchecked((long) 0x72CD5BE30DD5FCD3L) /* 2 */, unchecked((long) 0x6D019B93F6F97F3AL) /* 3 */,
+ unchecked((long) 0xCD9978FFD21F9193L) /* 4 */, unchecked((long) 0x7573A1C9708029E2L) /* 5 */,
+ unchecked((long) 0xB164326B922A83C3L) /* 6 */, unchecked((long) 0x46883EEE04915870L) /* 7 */,
+ unchecked((long) 0xEAACE3057103ECE6L) /* 8 */, unchecked((long) 0xC54169B808A3535CL) /* 9 */,
+ unchecked((long) 0x4CE754918DDEC47CL) /* 10 */, unchecked((long) 0x0AA2F4DFDC0DF40CL) /* 11 */,
+ unchecked((long) 0x10B76F18A74DBEFAL) /* 12 */, unchecked((long) 0xC6CCB6235AD1AB6AL) /* 13 */,
+ unchecked((long) 0x13726121572FE2FFL) /* 14 */, unchecked((long) 0x1A488C6F199D921EL) /* 15 */,
+ unchecked((long) 0x4BC9F9F4DA0007CAL) /* 16 */, unchecked((long) 0x26F5E6F6E85241C7L) /* 17 */,
+ unchecked((long) 0x859079DBEA5947B6L) /* 18 */, unchecked((long) 0x4F1885C5C99E8C92L) /* 19 */,
+ unchecked((long) 0xD78E761EA96F864BL) /* 20 */, unchecked((long) 0x8E36428C52B5C17DL) /* 21 */,
+ unchecked((long) 0x69CF6827373063C1L) /* 22 */, unchecked((long) 0xB607C93D9BB4C56EL) /* 23 */,
+ unchecked((long) 0x7D820E760E76B5EAL) /* 24 */, unchecked((long) 0x645C9CC6F07FDC42L) /* 25 */,
+ unchecked((long) 0xBF38A078243342E0L) /* 26 */, unchecked((long) 0x5F6B343C9D2E7D04L) /* 27 */,
+ unchecked((long) 0xF2C28AEB600B0EC6L) /* 28 */, unchecked((long) 0x6C0ED85F7254BCACL) /* 29 */,
+ unchecked((long) 0x71592281A4DB4FE5L) /* 30 */, unchecked((long) 0x1967FA69CE0FED9FL) /* 31 */,
+ unchecked((long) 0xFD5293F8B96545DBL) /* 32 */, unchecked((long) 0xC879E9D7F2A7600BL) /* 33 */,
+ unchecked((long) 0x860248920193194EL) /* 34 */, unchecked((long) 0xA4F9533B2D9CC0B3L) /* 35 */,
+ unchecked((long) 0x9053836C15957613L) /* 36 */, unchecked((long) 0xDB6DCF8AFC357BF1L) /* 37 */,
+ unchecked((long) 0x18BEEA7A7A370F57L) /* 38 */, unchecked((long) 0x037117CA50B99066L) /* 39 */,
+ unchecked((long) 0x6AB30A9774424A35L) /* 40 */, unchecked((long) 0xF4E92F02E325249BL) /* 41 */,
+ unchecked((long) 0x7739DB07061CCAE1L) /* 42 */, unchecked((long) 0xD8F3B49CECA42A05L) /* 43 */,
+ unchecked((long) 0xBD56BE3F51382F73L) /* 44 */, unchecked((long) 0x45FAED5843B0BB28L) /* 45 */,
+ unchecked((long) 0x1C813D5C11BF1F83L) /* 46 */, unchecked((long) 0x8AF0E4B6D75FA169L) /* 47 */,
+ unchecked((long) 0x33EE18A487AD9999L) /* 48 */, unchecked((long) 0x3C26E8EAB1C94410L) /* 49 */,
+ unchecked((long) 0xB510102BC0A822F9L) /* 50 */, unchecked((long) 0x141EEF310CE6123BL) /* 51 */,
+ unchecked((long) 0xFC65B90059DDB154L) /* 52 */, unchecked((long) 0xE0158640C5E0E607L) /* 53 */,
+ unchecked((long) 0x884E079826C3A3CFL) /* 54 */, unchecked((long) 0x930D0D9523C535FDL) /* 55 */,
+ unchecked((long) 0x35638D754E9A2B00L) /* 56 */, unchecked((long) 0x4085FCCF40469DD5L) /* 57 */,
+ unchecked((long) 0xC4B17AD28BE23A4CL) /* 58 */, unchecked((long) 0xCAB2F0FC6A3E6A2EL) /* 59 */,
+ unchecked((long) 0x2860971A6B943FCDL) /* 60 */, unchecked((long) 0x3DDE6EE212E30446L) /* 61 */,
+ unchecked((long) 0x6222F32AE01765AEL) /* 62 */, unchecked((long) 0x5D550BB5478308FEL) /* 63 */,
+ unchecked((long) 0xA9EFA98DA0EDA22AL) /* 64 */, unchecked((long) 0xC351A71686C40DA7L) /* 65 */,
+ unchecked((long) 0x1105586D9C867C84L) /* 66 */, unchecked((long) 0xDCFFEE85FDA22853L) /* 67 */,
+ unchecked((long) 0xCCFBD0262C5EEF76L) /* 68 */, unchecked((long) 0xBAF294CB8990D201L) /* 69 */,
+ unchecked((long) 0xE69464F52AFAD975L) /* 70 */, unchecked((long) 0x94B013AFDF133E14L) /* 71 */,
+ unchecked((long) 0x06A7D1A32823C958L) /* 72 */, unchecked((long) 0x6F95FE5130F61119L) /* 73 */,
+ unchecked((long) 0xD92AB34E462C06C0L) /* 74 */, unchecked((long) 0xED7BDE33887C71D2L) /* 75 */,
+ unchecked((long) 0x79746D6E6518393EL) /* 76 */, unchecked((long) 0x5BA419385D713329L) /* 77 */,
+ unchecked((long) 0x7C1BA6B948A97564L) /* 78 */, unchecked((long) 0x31987C197BFDAC67L) /* 79 */,
+ unchecked((long) 0xDE6C23C44B053D02L) /* 80 */, unchecked((long) 0x581C49FED002D64DL) /* 81 */,
+ unchecked((long) 0xDD474D6338261571L) /* 82 */, unchecked((long) 0xAA4546C3E473D062L) /* 83 */,
+ unchecked((long) 0x928FCE349455F860L) /* 84 */, unchecked((long) 0x48161BBACAAB94D9L) /* 85 */,
+ unchecked((long) 0x63912430770E6F68L) /* 86 */, unchecked((long) 0x6EC8A5E602C6641CL) /* 87 */,
+ unchecked((long) 0x87282515337DDD2BL) /* 88 */, unchecked((long) 0x2CDA6B42034B701BL) /* 89 */,
+ unchecked((long) 0xB03D37C181CB096DL) /* 90 */, unchecked((long) 0xE108438266C71C6FL) /* 91 */,
+ unchecked((long) 0x2B3180C7EB51B255L) /* 92 */, unchecked((long) 0xDF92B82F96C08BBCL) /* 93 */,
+ unchecked((long) 0x5C68C8C0A632F3BAL) /* 94 */, unchecked((long) 0x5504CC861C3D0556L) /* 95 */,
+ unchecked((long) 0xABBFA4E55FB26B8FL) /* 96 */, unchecked((long) 0x41848B0AB3BACEB4L) /* 97 */,
+ unchecked((long) 0xB334A273AA445D32L) /* 98 */, unchecked((long) 0xBCA696F0A85AD881L) /* 99 */,
+ unchecked((long) 0x24F6EC65B528D56CL) /* 100 */, unchecked((long) 0x0CE1512E90F4524AL) /* 101 */,
+ unchecked((long) 0x4E9DD79D5506D35AL) /* 102 */, unchecked((long) 0x258905FAC6CE9779L) /* 103 */,
+ unchecked((long) 0x2019295B3E109B33L) /* 104 */, unchecked((long) 0xF8A9478B73A054CCL) /* 105 */,
+ unchecked((long) 0x2924F2F934417EB0L) /* 106 */, unchecked((long) 0x3993357D536D1BC4L) /* 107 */,
+ unchecked((long) 0x38A81AC21DB6FF8BL) /* 108 */, unchecked((long) 0x47C4FBF17D6016BFL) /* 109 */,
+ unchecked((long) 0x1E0FAADD7667E3F5L) /* 110 */, unchecked((long) 0x7ABCFF62938BEB96L) /* 111 */,
+ unchecked((long) 0xA78DAD948FC179C9L) /* 112 */, unchecked((long) 0x8F1F98B72911E50DL) /* 113 */,
+ unchecked((long) 0x61E48EAE27121A91L) /* 114 */, unchecked((long) 0x4D62F7AD31859808L) /* 115 */,
+ unchecked((long) 0xECEBA345EF5CEAEBL) /* 116 */, unchecked((long) 0xF5CEB25EBC9684CEL) /* 117 */,
+ unchecked((long) 0xF633E20CB7F76221L) /* 118 */, unchecked((long) 0xA32CDF06AB8293E4L) /* 119 */,
+ unchecked((long) 0x985A202CA5EE2CA4L) /* 120 */, unchecked((long) 0xCF0B8447CC8A8FB1L) /* 121 */,
+ unchecked((long) 0x9F765244979859A3L) /* 122 */, unchecked((long) 0xA8D516B1A1240017L) /* 123 */,
+ unchecked((long) 0x0BD7BA3EBB5DC726L) /* 124 */, unchecked((long) 0xE54BCA55B86ADB39L) /* 125 */,
+ unchecked((long) 0x1D7A3AFD6C478063L) /* 126 */, unchecked((long) 0x519EC608E7669EDDL) /* 127 */,
+ unchecked((long) 0x0E5715A2D149AA23L) /* 128 */, unchecked((long) 0x177D4571848FF194L) /* 129 */,
+ unchecked((long) 0xEEB55F3241014C22L) /* 130 */, unchecked((long) 0x0F5E5CA13A6E2EC2L) /* 131 */,
+ unchecked((long) 0x8029927B75F5C361L) /* 132 */, unchecked((long) 0xAD139FABC3D6E436L) /* 133 */,
+ unchecked((long) 0x0D5DF1A94CCF402FL) /* 134 */, unchecked((long) 0x3E8BD948BEA5DFC8L) /* 135 */,
+ unchecked((long) 0xA5A0D357BD3FF77EL) /* 136 */, unchecked((long) 0xA2D12E251F74F645L) /* 137 */,
+ unchecked((long) 0x66FD9E525E81A082L) /* 138 */, unchecked((long) 0x2E0C90CE7F687A49L) /* 139 */,
+ unchecked((long) 0xC2E8BCBEBA973BC5L) /* 140 */, unchecked((long) 0x000001BCE509745FL) /* 141 */,
+ unchecked((long) 0x423777BBE6DAB3D6L) /* 142 */, unchecked((long) 0xD1661C7EAEF06EB5L) /* 143 */,
+ unchecked((long) 0xA1781F354DAACFD8L) /* 144 */, unchecked((long) 0x2D11284A2B16AFFCL) /* 145 */,
+ unchecked((long) 0xF1FC4F67FA891D1FL) /* 146 */, unchecked((long) 0x73ECC25DCB920ADAL) /* 147 */,
+ unchecked((long) 0xAE610C22C2A12651L) /* 148 */, unchecked((long) 0x96E0A810D356B78AL) /* 149 */,
+ unchecked((long) 0x5A9A381F2FE7870FL) /* 150 */, unchecked((long) 0xD5AD62EDE94E5530L) /* 151 */,
+ unchecked((long) 0xD225E5E8368D1427L) /* 152 */, unchecked((long) 0x65977B70C7AF4631L) /* 153 */,
+ unchecked((long) 0x99F889B2DE39D74FL) /* 154 */, unchecked((long) 0x233F30BF54E1D143L) /* 155 */,
+ unchecked((long) 0x9A9675D3D9A63C97L) /* 156 */, unchecked((long) 0x5470554FF334F9A8L) /* 157 */,
+ unchecked((long) 0x166ACB744A4F5688L) /* 158 */, unchecked((long) 0x70C74CAAB2E4AEADL) /* 159 */,
+ unchecked((long) 0xF0D091646F294D12L) /* 160 */, unchecked((long) 0x57B82A89684031D1L) /* 161 */,
+ unchecked((long) 0xEFD95A5A61BE0B6BL) /* 162 */, unchecked((long) 0x2FBD12E969F2F29AL) /* 163 */,
+ unchecked((long) 0x9BD37013FEFF9FE8L) /* 164 */, unchecked((long) 0x3F9B0404D6085A06L) /* 165 */,
+ unchecked((long) 0x4940C1F3166CFE15L) /* 166 */, unchecked((long) 0x09542C4DCDF3DEFBL) /* 167 */,
+ unchecked((long) 0xB4C5218385CD5CE3L) /* 168 */, unchecked((long) 0xC935B7DC4462A641L) /* 169 */,
+ unchecked((long) 0x3417F8A68ED3B63FL) /* 170 */, unchecked((long) 0xB80959295B215B40L) /* 171 */,
+ unchecked((long) 0xF99CDAEF3B8C8572L) /* 172 */, unchecked((long) 0x018C0614F8FCB95DL) /* 173 */,
+ unchecked((long) 0x1B14ACCD1A3ACDF3L) /* 174 */, unchecked((long) 0x84D471F200BB732DL) /* 175 */,
+ unchecked((long) 0xC1A3110E95E8DA16L) /* 176 */, unchecked((long) 0x430A7220BF1A82B8L) /* 177 */,
+ unchecked((long) 0xB77E090D39DF210EL) /* 178 */, unchecked((long) 0x5EF4BD9F3CD05E9DL) /* 179 */,
+ unchecked((long) 0x9D4FF6DA7E57A444L) /* 180 */, unchecked((long) 0xDA1D60E183D4A5F8L) /* 181 */,
+ unchecked((long) 0xB287C38417998E47L) /* 182 */, unchecked((long) 0xFE3EDC121BB31886L) /* 183 */,
+ unchecked((long) 0xC7FE3CCC980CCBEFL) /* 184 */, unchecked((long) 0xE46FB590189BFD03L) /* 185 */,
+ unchecked((long) 0x3732FD469A4C57DCL) /* 186 */, unchecked((long) 0x7EF700A07CF1AD65L) /* 187 */,
+ unchecked((long) 0x59C64468A31D8859L) /* 188 */, unchecked((long) 0x762FB0B4D45B61F6L) /* 189 */,
+ unchecked((long) 0x155BAED099047718L) /* 190 */, unchecked((long) 0x68755E4C3D50BAA6L) /* 191 */,
+ unchecked((long) 0xE9214E7F22D8B4DFL) /* 192 */, unchecked((long) 0x2ADDBF532EAC95F4L) /* 193 */,
+ unchecked((long) 0x32AE3909B4BD0109L) /* 194 */, unchecked((long) 0x834DF537B08E3450L) /* 195 */,
+ unchecked((long) 0xFA209DA84220728DL) /* 196 */, unchecked((long) 0x9E691D9B9EFE23F7L) /* 197 */,
+ unchecked((long) 0x0446D288C4AE8D7FL) /* 198 */, unchecked((long) 0x7B4CC524E169785BL) /* 199 */,
+ unchecked((long) 0x21D87F0135CA1385L) /* 200 */, unchecked((long) 0xCEBB400F137B8AA5L) /* 201 */,
+ unchecked((long) 0x272E2B66580796BEL) /* 202 */, unchecked((long) 0x3612264125C2B0DEL) /* 203 */,
+ unchecked((long) 0x057702BDAD1EFBB2L) /* 204 */, unchecked((long) 0xD4BABB8EACF84BE9L) /* 205 */,
+ unchecked((long) 0x91583139641BC67BL) /* 206 */, unchecked((long) 0x8BDC2DE08036E024L) /* 207 */,
+ unchecked((long) 0x603C8156F49F68EDL) /* 208 */, unchecked((long) 0xF7D236F7DBEF5111L) /* 209 */,
+ unchecked((long) 0x9727C4598AD21E80L) /* 210 */, unchecked((long) 0xA08A0896670A5FD7L) /* 211 */,
+ unchecked((long) 0xCB4A8F4309EBA9CBL) /* 212 */, unchecked((long) 0x81AF564B0F7036A1L) /* 213 */,
+ unchecked((long) 0xC0B99AA778199ABDL) /* 214 */, unchecked((long) 0x959F1EC83FC8E952L) /* 215 */,
+ unchecked((long) 0x8C505077794A81B9L) /* 216 */, unchecked((long) 0x3ACAAF8F056338F0L) /* 217 */,
+ unchecked((long) 0x07B43F50627A6778L) /* 218 */, unchecked((long) 0x4A44AB49F5ECCC77L) /* 219 */,
+ unchecked((long) 0x3BC3D6E4B679EE98L) /* 220 */, unchecked((long) 0x9CC0D4D1CF14108CL) /* 221 */,
+ unchecked((long) 0x4406C00B206BC8A0L) /* 222 */, unchecked((long) 0x82A18854C8D72D89L) /* 223 */,
+ unchecked((long) 0x67E366B35C3C432CL) /* 224 */, unchecked((long) 0xB923DD61102B37F2L) /* 225 */,
+ unchecked((long) 0x56AB2779D884271DL) /* 226 */, unchecked((long) 0xBE83E1B0FF1525AFL) /* 227 */,
+ unchecked((long) 0xFB7C65D4217E49A9L) /* 228 */, unchecked((long) 0x6BDBE0E76D48E7D4L) /* 229 */,
+ unchecked((long) 0x08DF828745D9179EL) /* 230 */, unchecked((long) 0x22EA6A9ADD53BD34L) /* 231 */,
+ unchecked((long) 0xE36E141C5622200AL) /* 232 */, unchecked((long) 0x7F805D1B8CB750EEL) /* 233 */,
+ unchecked((long) 0xAFE5C7A59F58E837L) /* 234 */, unchecked((long) 0xE27F996A4FB1C23CL) /* 235 */,
+ unchecked((long) 0xD3867DFB0775F0D0L) /* 236 */, unchecked((long) 0xD0E673DE6E88891AL) /* 237 */,
+ unchecked((long) 0x123AEB9EAFB86C25L) /* 238 */, unchecked((long) 0x30F1D5D5C145B895L) /* 239 */,
+ unchecked((long) 0xBB434A2DEE7269E7L) /* 240 */, unchecked((long) 0x78CB67ECF931FA38L) /* 241 */,
+ unchecked((long) 0xF33B0372323BBF9CL) /* 242 */, unchecked((long) 0x52D66336FB279C74L) /* 243 */,
+ unchecked((long) 0x505F33AC0AFB4EAAL) /* 244 */, unchecked((long) 0xE8A5CD99A2CCE187L) /* 245 */,
+ unchecked((long) 0x534974801E2D30BBL) /* 246 */, unchecked((long) 0x8D2D5711D5876D90L) /* 247 */,
+ unchecked((long) 0x1F1A412891BC038EL) /* 248 */, unchecked((long) 0xD6E2E71D82E56648L) /* 249 */,
+ unchecked((long) 0x74036C3A497732B7L) /* 250 */, unchecked((long) 0x89B67ED96361F5ABL) /* 251 */,
+ unchecked((long) 0xFFED95D8F1EA02A2L) /* 252 */, unchecked((long) 0xE72B3BD61464D43DL) /* 253 */,
+ unchecked((long) 0xA6300F170BDC4820L) /* 254 */, unchecked((long) 0xEBC18760ED78A77AL) /* 255 */,
+ };
+
+ private static readonly long[] t2 = {
+ unchecked((long) 0xE6A6BE5A05A12138L) /* 256 */, unchecked((long) 0xB5A122A5B4F87C98L) /* 257 */,
+ unchecked((long) 0x563C6089140B6990L) /* 258 */, unchecked((long) 0x4C46CB2E391F5DD5L) /* 259 */,
+ unchecked((long) 0xD932ADDBC9B79434L) /* 260 */, unchecked((long) 0x08EA70E42015AFF5L) /* 261 */,
+ unchecked((long) 0xD765A6673E478CF1L) /* 262 */, unchecked((long) 0xC4FB757EAB278D99L) /* 263 */,
+ unchecked((long) 0xDF11C6862D6E0692L) /* 264 */, unchecked((long) 0xDDEB84F10D7F3B16L) /* 265 */,
+ unchecked((long) 0x6F2EF604A665EA04L) /* 266 */, unchecked((long) 0x4A8E0F0FF0E0DFB3L) /* 267 */,
+ unchecked((long) 0xA5EDEEF83DBCBA51L) /* 268 */, unchecked((long) 0xFC4F0A2A0EA4371EL) /* 269 */,
+ unchecked((long) 0xE83E1DA85CB38429L) /* 270 */, unchecked((long) 0xDC8FF882BA1B1CE2L) /* 271 */,
+ unchecked((long) 0xCD45505E8353E80DL) /* 272 */, unchecked((long) 0x18D19A00D4DB0717L) /* 273 */,
+ unchecked((long) 0x34A0CFEDA5F38101L) /* 274 */, unchecked((long) 0x0BE77E518887CAF2L) /* 275 */,
+ unchecked((long) 0x1E341438B3C45136L) /* 276 */, unchecked((long) 0xE05797F49089CCF9L) /* 277 */,
+ unchecked((long) 0xFFD23F9DF2591D14L) /* 278 */, unchecked((long) 0x543DDA228595C5CDL) /* 279 */,
+ unchecked((long) 0x661F81FD99052A33L) /* 280 */, unchecked((long) 0x8736E641DB0F7B76L) /* 281 */,
+ unchecked((long) 0x15227725418E5307L) /* 282 */, unchecked((long) 0xE25F7F46162EB2FAL) /* 283 */,
+ unchecked((long) 0x48A8B2126C13D9FEL) /* 284 */, unchecked((long) 0xAFDC541792E76EEAL) /* 285 */,
+ unchecked((long) 0x03D912BFC6D1898FL) /* 286 */, unchecked((long) 0x31B1AAFA1B83F51BL) /* 287 */,
+ unchecked((long) 0xF1AC2796E42AB7D9L) /* 288 */, unchecked((long) 0x40A3A7D7FCD2EBACL) /* 289 */,
+ unchecked((long) 0x1056136D0AFBBCC5L) /* 290 */, unchecked((long) 0x7889E1DD9A6D0C85L) /* 291 */,
+ unchecked((long) 0xD33525782A7974AAL) /* 292 */, unchecked((long) 0xA7E25D09078AC09BL) /* 293 */,
+ unchecked((long) 0xBD4138B3EAC6EDD0L) /* 294 */, unchecked((long) 0x920ABFBE71EB9E70L) /* 295 */,
+ unchecked((long) 0xA2A5D0F54FC2625CL) /* 296 */, unchecked((long) 0xC054E36B0B1290A3L) /* 297 */,
+ unchecked((long) 0xF6DD59FF62FE932BL) /* 298 */, unchecked((long) 0x3537354511A8AC7DL) /* 299 */,
+ unchecked((long) 0xCA845E9172FADCD4L) /* 300 */, unchecked((long) 0x84F82B60329D20DCL) /* 301 */,
+ unchecked((long) 0x79C62CE1CD672F18L) /* 302 */, unchecked((long) 0x8B09A2ADD124642CL) /* 303 */,
+ unchecked((long) 0xD0C1E96A19D9E726L) /* 304 */, unchecked((long) 0x5A786A9B4BA9500CL) /* 305 */,
+ unchecked((long) 0x0E020336634C43F3L) /* 306 */, unchecked((long) 0xC17B474AEB66D822L) /* 307 */,
+ unchecked((long) 0x6A731AE3EC9BAAC2L) /* 308 */, unchecked((long) 0x8226667AE0840258L) /* 309 */,
+ unchecked((long) 0x67D4567691CAECA5L) /* 310 */, unchecked((long) 0x1D94155C4875ADB5L) /* 311 */,
+ unchecked((long) 0x6D00FD985B813FDFL) /* 312 */, unchecked((long) 0x51286EFCB774CD06L) /* 313 */,
+ unchecked((long) 0x5E8834471FA744AFL) /* 314 */, unchecked((long) 0xF72CA0AEE761AE2EL) /* 315 */,
+ unchecked((long) 0xBE40E4CDAEE8E09AL) /* 316 */, unchecked((long) 0xE9970BBB5118F665L) /* 317 */,
+ unchecked((long) 0x726E4BEB33DF1964L) /* 318 */, unchecked((long) 0x703B000729199762L) /* 319 */,
+ unchecked((long) 0x4631D816F5EF30A7L) /* 320 */, unchecked((long) 0xB880B5B51504A6BEL) /* 321 */,
+ unchecked((long) 0x641793C37ED84B6CL) /* 322 */, unchecked((long) 0x7B21ED77F6E97D96L) /* 323 */,
+ unchecked((long) 0x776306312EF96B73L) /* 324 */, unchecked((long) 0xAE528948E86FF3F4L) /* 325 */,
+ unchecked((long) 0x53DBD7F286A3F8F8L) /* 326 */, unchecked((long) 0x16CADCE74CFC1063L) /* 327 */,
+ unchecked((long) 0x005C19BDFA52C6DDL) /* 328 */, unchecked((long) 0x68868F5D64D46AD3L) /* 329 */,
+ unchecked((long) 0x3A9D512CCF1E186AL) /* 330 */, unchecked((long) 0x367E62C2385660AEL) /* 331 */,
+ unchecked((long) 0xE359E7EA77DCB1D7L) /* 332 */, unchecked((long) 0x526C0773749ABE6EL) /* 333 */,
+ unchecked((long) 0x735AE5F9D09F734BL) /* 334 */, unchecked((long) 0x493FC7CC8A558BA8L) /* 335 */,
+ unchecked((long) 0xB0B9C1533041AB45L) /* 336 */, unchecked((long) 0x321958BA470A59BDL) /* 337 */,
+ unchecked((long) 0x852DB00B5F46C393L) /* 338 */, unchecked((long) 0x91209B2BD336B0E5L) /* 339 */,
+ unchecked((long) 0x6E604F7D659EF19FL) /* 340 */, unchecked((long) 0xB99A8AE2782CCB24L) /* 341 */,
+ unchecked((long) 0xCCF52AB6C814C4C7L) /* 342 */, unchecked((long) 0x4727D9AFBE11727BL) /* 343 */,
+ unchecked((long) 0x7E950D0C0121B34DL) /* 344 */, unchecked((long) 0x756F435670AD471FL) /* 345 */,
+ unchecked((long) 0xF5ADD442615A6849L) /* 346 */, unchecked((long) 0x4E87E09980B9957AL) /* 347 */,
+ unchecked((long) 0x2ACFA1DF50AEE355L) /* 348 */, unchecked((long) 0xD898263AFD2FD556L) /* 349 */,
+ unchecked((long) 0xC8F4924DD80C8FD6L) /* 350 */, unchecked((long) 0xCF99CA3D754A173AL) /* 351 */,
+ unchecked((long) 0xFE477BACAF91BF3CL) /* 352 */, unchecked((long) 0xED5371F6D690C12DL) /* 353 */,
+ unchecked((long) 0x831A5C285E687094L) /* 354 */, unchecked((long) 0xC5D3C90A3708A0A4L) /* 355 */,
+ unchecked((long) 0x0F7F903717D06580L) /* 356 */, unchecked((long) 0x19F9BB13B8FDF27FL) /* 357 */,
+ unchecked((long) 0xB1BD6F1B4D502843L) /* 358 */, unchecked((long) 0x1C761BA38FFF4012L) /* 359 */,
+ unchecked((long) 0x0D1530C4E2E21F3BL) /* 360 */, unchecked((long) 0x8943CE69A7372C8AL) /* 361 */,
+ unchecked((long) 0xE5184E11FEB5CE66L) /* 362 */, unchecked((long) 0x618BDB80BD736621L) /* 363 */,
+ unchecked((long) 0x7D29BAD68B574D0BL) /* 364 */, unchecked((long) 0x81BB613E25E6FE5BL) /* 365 */,
+ unchecked((long) 0x071C9C10BC07913FL) /* 366 */, unchecked((long) 0xC7BEEB7909AC2D97L) /* 367 */,
+ unchecked((long) 0xC3E58D353BC5D757L) /* 368 */, unchecked((long) 0xEB017892F38F61E8L) /* 369 */,
+ unchecked((long) 0xD4EFFB9C9B1CC21AL) /* 370 */, unchecked((long) 0x99727D26F494F7ABL) /* 371 */,
+ unchecked((long) 0xA3E063A2956B3E03L) /* 372 */, unchecked((long) 0x9D4A8B9A4AA09C30L) /* 373 */,
+ unchecked((long) 0x3F6AB7D500090FB4L) /* 374 */, unchecked((long) 0x9CC0F2A057268AC0L) /* 375 */,
+ unchecked((long) 0x3DEE9D2DEDBF42D1L) /* 376 */, unchecked((long) 0x330F49C87960A972L) /* 377 */,
+ unchecked((long) 0xC6B2720287421B41L) /* 378 */, unchecked((long) 0x0AC59EC07C00369CL) /* 379 */,
+ unchecked((long) 0xEF4EAC49CB353425L) /* 380 */, unchecked((long) 0xF450244EEF0129D8L) /* 381 */,
+ unchecked((long) 0x8ACC46E5CAF4DEB6L) /* 382 */, unchecked((long) 0x2FFEAB63989263F7L) /* 383 */,
+ unchecked((long) 0x8F7CB9FE5D7A4578L) /* 384 */, unchecked((long) 0x5BD8F7644E634635L) /* 385 */,
+ unchecked((long) 0x427A7315BF2DC900L) /* 386 */, unchecked((long) 0x17D0C4AA2125261CL) /* 387 */,
+ unchecked((long) 0x3992486C93518E50L) /* 388 */, unchecked((long) 0xB4CBFEE0A2D7D4C3L) /* 389 */,
+ unchecked((long) 0x7C75D6202C5DDD8DL) /* 390 */, unchecked((long) 0xDBC295D8E35B6C61L) /* 391 */,
+ unchecked((long) 0x60B369D302032B19L) /* 392 */, unchecked((long) 0xCE42685FDCE44132L) /* 393 */,
+ unchecked((long) 0x06F3DDB9DDF65610L) /* 394 */, unchecked((long) 0x8EA4D21DB5E148F0L) /* 395 */,
+ unchecked((long) 0x20B0FCE62FCD496FL) /* 396 */, unchecked((long) 0x2C1B912358B0EE31L) /* 397 */,
+ unchecked((long) 0xB28317B818F5A308L) /* 398 */, unchecked((long) 0xA89C1E189CA6D2CFL) /* 399 */,
+ unchecked((long) 0x0C6B18576AAADBC8L) /* 400 */, unchecked((long) 0xB65DEAA91299FAE3L) /* 401 */,
+ unchecked((long) 0xFB2B794B7F1027E7L) /* 402 */, unchecked((long) 0x04E4317F443B5BEBL) /* 403 */,
+ unchecked((long) 0x4B852D325939D0A6L) /* 404 */, unchecked((long) 0xD5AE6BEEFB207FFCL) /* 405 */,
+ unchecked((long) 0x309682B281C7D374L) /* 406 */, unchecked((long) 0xBAE309A194C3B475L) /* 407 */,
+ unchecked((long) 0x8CC3F97B13B49F05L) /* 408 */, unchecked((long) 0x98A9422FF8293967L) /* 409 */,
+ unchecked((long) 0x244B16B01076FF7CL) /* 410 */, unchecked((long) 0xF8BF571C663D67EEL) /* 411 */,
+ unchecked((long) 0x1F0D6758EEE30DA1L) /* 412 */, unchecked((long) 0xC9B611D97ADEB9B7L) /* 413 */,
+ unchecked((long) 0xB7AFD5887B6C57A2L) /* 414 */, unchecked((long) 0x6290AE846B984FE1L) /* 415 */,
+ unchecked((long) 0x94DF4CDEACC1A5FDL) /* 416 */, unchecked((long) 0x058A5BD1C5483AFFL) /* 417 */,
+ unchecked((long) 0x63166CC142BA3C37L) /* 418 */, unchecked((long) 0x8DB8526EB2F76F40L) /* 419 */,
+ unchecked((long) 0xE10880036F0D6D4EL) /* 420 */, unchecked((long) 0x9E0523C9971D311DL) /* 421 */,
+ unchecked((long) 0x45EC2824CC7CD691L) /* 422 */, unchecked((long) 0x575B8359E62382C9L) /* 423 */,
+ unchecked((long) 0xFA9E400DC4889995L) /* 424 */, unchecked((long) 0xD1823ECB45721568L) /* 425 */,
+ unchecked((long) 0xDAFD983B8206082FL) /* 426 */, unchecked((long) 0xAA7D29082386A8CBL) /* 427 */,
+ unchecked((long) 0x269FCD4403B87588L) /* 428 */, unchecked((long) 0x1B91F5F728BDD1E0L) /* 429 */,
+ unchecked((long) 0xE4669F39040201F6L) /* 430 */, unchecked((long) 0x7A1D7C218CF04ADEL) /* 431 */,
+ unchecked((long) 0x65623C29D79CE5CEL) /* 432 */, unchecked((long) 0x2368449096C00BB1L) /* 433 */,
+ unchecked((long) 0xAB9BF1879DA503BAL) /* 434 */, unchecked((long) 0xBC23ECB1A458058EL) /* 435 */,
+ unchecked((long) 0x9A58DF01BB401ECCL) /* 436 */, unchecked((long) 0xA070E868A85F143DL) /* 437 */,
+ unchecked((long) 0x4FF188307DF2239EL) /* 438 */, unchecked((long) 0x14D565B41A641183L) /* 439 */,
+ unchecked((long) 0xEE13337452701602L) /* 440 */, unchecked((long) 0x950E3DCF3F285E09L) /* 441 */,
+ unchecked((long) 0x59930254B9C80953L) /* 442 */, unchecked((long) 0x3BF299408930DA6DL) /* 443 */,
+ unchecked((long) 0xA955943F53691387L) /* 444 */, unchecked((long) 0xA15EDECAA9CB8784L) /* 445 */,
+ unchecked((long) 0x29142127352BE9A0L) /* 446 */, unchecked((long) 0x76F0371FFF4E7AFBL) /* 447 */,
+ unchecked((long) 0x0239F450274F2228L) /* 448 */, unchecked((long) 0xBB073AF01D5E868BL) /* 449 */,
+ unchecked((long) 0xBFC80571C10E96C1L) /* 450 */, unchecked((long) 0xD267088568222E23L) /* 451 */,
+ unchecked((long) 0x9671A3D48E80B5B0L) /* 452 */, unchecked((long) 0x55B5D38AE193BB81L) /* 453 */,
+ unchecked((long) 0x693AE2D0A18B04B8L) /* 454 */, unchecked((long) 0x5C48B4ECADD5335FL) /* 455 */,
+ unchecked((long) 0xFD743B194916A1CAL) /* 456 */, unchecked((long) 0x2577018134BE98C4L) /* 457 */,
+ unchecked((long) 0xE77987E83C54A4ADL) /* 458 */, unchecked((long) 0x28E11014DA33E1B9L) /* 459 */,
+ unchecked((long) 0x270CC59E226AA213L) /* 460 */, unchecked((long) 0x71495F756D1A5F60L) /* 461 */,
+ unchecked((long) 0x9BE853FB60AFEF77L) /* 462 */, unchecked((long) 0xADC786A7F7443DBFL) /* 463 */,
+ unchecked((long) 0x0904456173B29A82L) /* 464 */, unchecked((long) 0x58BC7A66C232BD5EL) /* 465 */,
+ unchecked((long) 0xF306558C673AC8B2L) /* 466 */, unchecked((long) 0x41F639C6B6C9772AL) /* 467 */,
+ unchecked((long) 0x216DEFE99FDA35DAL) /* 468 */, unchecked((long) 0x11640CC71C7BE615L) /* 469 */,
+ unchecked((long) 0x93C43694565C5527L) /* 470 */, unchecked((long) 0xEA038E6246777839L) /* 471 */,
+ unchecked((long) 0xF9ABF3CE5A3E2469L) /* 472 */, unchecked((long) 0x741E768D0FD312D2L) /* 473 */,
+ unchecked((long) 0x0144B883CED652C6L) /* 474 */, unchecked((long) 0xC20B5A5BA33F8552L) /* 475 */,
+ unchecked((long) 0x1AE69633C3435A9DL) /* 476 */, unchecked((long) 0x97A28CA4088CFDECL) /* 477 */,
+ unchecked((long) 0x8824A43C1E96F420L) /* 478 */, unchecked((long) 0x37612FA66EEEA746L) /* 479 */,
+ unchecked((long) 0x6B4CB165F9CF0E5AL) /* 480 */, unchecked((long) 0x43AA1C06A0ABFB4AL) /* 481 */,
+ unchecked((long) 0x7F4DC26FF162796BL) /* 482 */, unchecked((long) 0x6CBACC8E54ED9B0FL) /* 483 */,
+ unchecked((long) 0xA6B7FFEFD2BB253EL) /* 484 */, unchecked((long) 0x2E25BC95B0A29D4FL) /* 485 */,
+ unchecked((long) 0x86D6A58BDEF1388CL) /* 486 */, unchecked((long) 0xDED74AC576B6F054L) /* 487 */,
+ unchecked((long) 0x8030BDBC2B45805DL) /* 488 */, unchecked((long) 0x3C81AF70E94D9289L) /* 489 */,
+ unchecked((long) 0x3EFF6DDA9E3100DBL) /* 490 */, unchecked((long) 0xB38DC39FDFCC8847L) /* 491 */,
+ unchecked((long) 0x123885528D17B87EL) /* 492 */, unchecked((long) 0xF2DA0ED240B1B642L) /* 493 */,
+ unchecked((long) 0x44CEFADCD54BF9A9L) /* 494 */, unchecked((long) 0x1312200E433C7EE6L) /* 495 */,
+ unchecked((long) 0x9FFCC84F3A78C748L) /* 496 */, unchecked((long) 0xF0CD1F72248576BBL) /* 497 */,
+ unchecked((long) 0xEC6974053638CFE4L) /* 498 */, unchecked((long) 0x2BA7B67C0CEC4E4CL) /* 499 */,
+ unchecked((long) 0xAC2F4DF3E5CE32EDL) /* 500 */, unchecked((long) 0xCB33D14326EA4C11L) /* 501 */,
+ unchecked((long) 0xA4E9044CC77E58BCL) /* 502 */, unchecked((long) 0x5F513293D934FCEFL) /* 503 */,
+ unchecked((long) 0x5DC9645506E55444L) /* 504 */, unchecked((long) 0x50DE418F317DE40AL) /* 505 */,
+ unchecked((long) 0x388CB31A69DDE259L) /* 506 */, unchecked((long) 0x2DB4A83455820A86L) /* 507 */,
+ unchecked((long) 0x9010A91E84711AE9L) /* 508 */, unchecked((long) 0x4DF7F0B7B1498371L) /* 509 */,
+ unchecked((long) 0xD62A2EABC0977179L) /* 510 */, unchecked((long) 0x22FAC097AA8D5C0EL) /* 511 */,
+ };
+
+ private static readonly long[] t3 = {
+ unchecked((long) 0xF49FCC2FF1DAF39BL) /* 512 */, unchecked((long) 0x487FD5C66FF29281L) /* 513 */,
+ unchecked((long) 0xE8A30667FCDCA83FL) /* 514 */, unchecked((long) 0x2C9B4BE3D2FCCE63L) /* 515 */,
+ unchecked((long) 0xDA3FF74B93FBBBC2L) /* 516 */, unchecked((long) 0x2FA165D2FE70BA66L) /* 517 */,
+ unchecked((long) 0xA103E279970E93D4L) /* 518 */, unchecked((long) 0xBECDEC77B0E45E71L) /* 519 */,
+ unchecked((long) 0xCFB41E723985E497L) /* 520 */, unchecked((long) 0xB70AAA025EF75017L) /* 521 */,
+ unchecked((long) 0xD42309F03840B8E0L) /* 522 */, unchecked((long) 0x8EFC1AD035898579L) /* 523 */,
+ unchecked((long) 0x96C6920BE2B2ABC5L) /* 524 */, unchecked((long) 0x66AF4163375A9172L) /* 525 */,
+ unchecked((long) 0x2174ABDCCA7127FBL) /* 526 */, unchecked((long) 0xB33CCEA64A72FF41L) /* 527 */,
+ unchecked((long) 0xF04A4933083066A5L) /* 528 */, unchecked((long) 0x8D970ACDD7289AF5L) /* 529 */,
+ unchecked((long) 0x8F96E8E031C8C25EL) /* 530 */, unchecked((long) 0xF3FEC02276875D47L) /* 531 */,
+ unchecked((long) 0xEC7BF310056190DDL) /* 532 */, unchecked((long) 0xF5ADB0AEBB0F1491L) /* 533 */,
+ unchecked((long) 0x9B50F8850FD58892L) /* 534 */, unchecked((long) 0x4975488358B74DE8L) /* 535 */,
+ unchecked((long) 0xA3354FF691531C61L) /* 536 */, unchecked((long) 0x0702BBE481D2C6EEL) /* 537 */,
+ unchecked((long) 0x89FB24057DEDED98L) /* 538 */, unchecked((long) 0xAC3075138596E902L) /* 539 */,
+ unchecked((long) 0x1D2D3580172772EDL) /* 540 */, unchecked((long) 0xEB738FC28E6BC30DL) /* 541 */,
+ unchecked((long) 0x5854EF8F63044326L) /* 542 */, unchecked((long) 0x9E5C52325ADD3BBEL) /* 543 */,
+ unchecked((long) 0x90AA53CF325C4623L) /* 544 */, unchecked((long) 0xC1D24D51349DD067L) /* 545 */,
+ unchecked((long) 0x2051CFEEA69EA624L) /* 546 */, unchecked((long) 0x13220F0A862E7E4FL) /* 547 */,
+ unchecked((long) 0xCE39399404E04864L) /* 548 */, unchecked((long) 0xD9C42CA47086FCB7L) /* 549 */,
+ unchecked((long) 0x685AD2238A03E7CCL) /* 550 */, unchecked((long) 0x066484B2AB2FF1DBL) /* 551 */,
+ unchecked((long) 0xFE9D5D70EFBF79ECL) /* 552 */, unchecked((long) 0x5B13B9DD9C481854L) /* 553 */,
+ unchecked((long) 0x15F0D475ED1509ADL) /* 554 */, unchecked((long) 0x0BEBCD060EC79851L) /* 555 */,
+ unchecked((long) 0xD58C6791183AB7F8L) /* 556 */, unchecked((long) 0xD1187C5052F3EEE4L) /* 557 */,
+ unchecked((long) 0xC95D1192E54E82FFL) /* 558 */, unchecked((long) 0x86EEA14CB9AC6CA2L) /* 559 */,
+ unchecked((long) 0x3485BEB153677D5DL) /* 560 */, unchecked((long) 0xDD191D781F8C492AL) /* 561 */,
+ unchecked((long) 0xF60866BAA784EBF9L) /* 562 */, unchecked((long) 0x518F643BA2D08C74L) /* 563 */,
+ unchecked((long) 0x8852E956E1087C22L) /* 564 */, unchecked((long) 0xA768CB8DC410AE8DL) /* 565 */,
+ unchecked((long) 0x38047726BFEC8E1AL) /* 566 */, unchecked((long) 0xA67738B4CD3B45AAL) /* 567 */,
+ unchecked((long) 0xAD16691CEC0DDE19L) /* 568 */, unchecked((long) 0xC6D4319380462E07L) /* 569 */,
+ unchecked((long) 0xC5A5876D0BA61938L) /* 570 */, unchecked((long) 0x16B9FA1FA58FD840L) /* 571 */,
+ unchecked((long) 0x188AB1173CA74F18L) /* 572 */, unchecked((long) 0xABDA2F98C99C021FL) /* 573 */,
+ unchecked((long) 0x3E0580AB134AE816L) /* 574 */, unchecked((long) 0x5F3B05B773645ABBL) /* 575 */,
+ unchecked((long) 0x2501A2BE5575F2F6L) /* 576 */, unchecked((long) 0x1B2F74004E7E8BA9L) /* 577 */,
+ unchecked((long) 0x1CD7580371E8D953L) /* 578 */, unchecked((long) 0x7F6ED89562764E30L) /* 579 */,
+ unchecked((long) 0xB15926FF596F003DL) /* 580 */, unchecked((long) 0x9F65293DA8C5D6B9L) /* 581 */,
+ unchecked((long) 0x6ECEF04DD690F84CL) /* 582 */, unchecked((long) 0x4782275FFF33AF88L) /* 583 */,
+ unchecked((long) 0xE41433083F820801L) /* 584 */, unchecked((long) 0xFD0DFE409A1AF9B5L) /* 585 */,
+ unchecked((long) 0x4325A3342CDB396BL) /* 586 */, unchecked((long) 0x8AE77E62B301B252L) /* 587 */,
+ unchecked((long) 0xC36F9E9F6655615AL) /* 588 */, unchecked((long) 0x85455A2D92D32C09L) /* 589 */,
+ unchecked((long) 0xF2C7DEA949477485L) /* 590 */, unchecked((long) 0x63CFB4C133A39EBAL) /* 591 */,
+ unchecked((long) 0x83B040CC6EBC5462L) /* 592 */, unchecked((long) 0x3B9454C8FDB326B0L) /* 593 */,
+ unchecked((long) 0x56F56A9E87FFD78CL) /* 594 */, unchecked((long) 0x2DC2940D99F42BC6L) /* 595 */,
+ unchecked((long) 0x98F7DF096B096E2DL) /* 596 */, unchecked((long) 0x19A6E01E3AD852BFL) /* 597 */,
+ unchecked((long) 0x42A99CCBDBD4B40BL) /* 598 */, unchecked((long) 0xA59998AF45E9C559L) /* 599 */,
+ unchecked((long) 0x366295E807D93186L) /* 600 */, unchecked((long) 0x6B48181BFAA1F773L) /* 601 */,
+ unchecked((long) 0x1FEC57E2157A0A1DL) /* 602 */, unchecked((long) 0x4667446AF6201AD5L) /* 603 */,
+ unchecked((long) 0xE615EBCACFB0F075L) /* 604 */, unchecked((long) 0xB8F31F4F68290778L) /* 605 */,
+ unchecked((long) 0x22713ED6CE22D11EL) /* 606 */, unchecked((long) 0x3057C1A72EC3C93BL) /* 607 */,
+ unchecked((long) 0xCB46ACC37C3F1F2FL) /* 608 */, unchecked((long) 0xDBB893FD02AAF50EL) /* 609 */,
+ unchecked((long) 0x331FD92E600B9FCFL) /* 610 */, unchecked((long) 0xA498F96148EA3AD6L) /* 611 */,
+ unchecked((long) 0xA8D8426E8B6A83EAL) /* 612 */, unchecked((long) 0xA089B274B7735CDCL) /* 613 */,
+ unchecked((long) 0x87F6B3731E524A11L) /* 614 */, unchecked((long) 0x118808E5CBC96749L) /* 615 */,
+ unchecked((long) 0x9906E4C7B19BD394L) /* 616 */, unchecked((long) 0xAFED7F7E9B24A20CL) /* 617 */,
+ unchecked((long) 0x6509EADEEB3644A7L) /* 618 */, unchecked((long) 0x6C1EF1D3E8EF0EDEL) /* 619 */,
+ unchecked((long) 0xB9C97D43E9798FB4L) /* 620 */, unchecked((long) 0xA2F2D784740C28A3L) /* 621 */,
+ unchecked((long) 0x7B8496476197566FL) /* 622 */, unchecked((long) 0x7A5BE3E6B65F069DL) /* 623 */,
+ unchecked((long) 0xF96330ED78BE6F10L) /* 624 */, unchecked((long) 0xEEE60DE77A076A15L) /* 625 */,
+ unchecked((long) 0x2B4BEE4AA08B9BD0L) /* 626 */, unchecked((long) 0x6A56A63EC7B8894EL) /* 627 */,
+ unchecked((long) 0x02121359BA34FEF4L) /* 628 */, unchecked((long) 0x4CBF99F8283703FCL) /* 629 */,
+ unchecked((long) 0x398071350CAF30C8L) /* 630 */, unchecked((long) 0xD0A77A89F017687AL) /* 631 */,
+ unchecked((long) 0xF1C1A9EB9E423569L) /* 632 */, unchecked((long) 0x8C7976282DEE8199L) /* 633 */,
+ unchecked((long) 0x5D1737A5DD1F7ABDL) /* 634 */, unchecked((long) 0x4F53433C09A9FA80L) /* 635 */,
+ unchecked((long) 0xFA8B0C53DF7CA1D9L) /* 636 */, unchecked((long) 0x3FD9DCBC886CCB77L) /* 637 */,
+ unchecked((long) 0xC040917CA91B4720L) /* 638 */, unchecked((long) 0x7DD00142F9D1DCDFL) /* 639 */,
+ unchecked((long) 0x8476FC1D4F387B58L) /* 640 */, unchecked((long) 0x23F8E7C5F3316503L) /* 641 */,
+ unchecked((long) 0x032A2244E7E37339L) /* 642 */, unchecked((long) 0x5C87A5D750F5A74BL) /* 643 */,
+ unchecked((long) 0x082B4CC43698992EL) /* 644 */, unchecked((long) 0xDF917BECB858F63CL) /* 645 */,
+ unchecked((long) 0x3270B8FC5BF86DDAL) /* 646 */, unchecked((long) 0x10AE72BB29B5DD76L) /* 647 */,
+ unchecked((long) 0x576AC94E7700362BL) /* 648 */, unchecked((long) 0x1AD112DAC61EFB8FL) /* 649 */,
+ unchecked((long) 0x691BC30EC5FAA427L) /* 650 */, unchecked((long) 0xFF246311CC327143L) /* 651 */,
+ unchecked((long) 0x3142368E30E53206L) /* 652 */, unchecked((long) 0x71380E31E02CA396L) /* 653 */,
+ unchecked((long) 0x958D5C960AAD76F1L) /* 654 */, unchecked((long) 0xF8D6F430C16DA536L) /* 655 */,
+ unchecked((long) 0xC8FFD13F1BE7E1D2L) /* 656 */, unchecked((long) 0x7578AE66004DDBE1L) /* 657 */,
+ unchecked((long) 0x05833F01067BE646L) /* 658 */, unchecked((long) 0xBB34B5AD3BFE586DL) /* 659 */,
+ unchecked((long) 0x095F34C9A12B97F0L) /* 660 */, unchecked((long) 0x247AB64525D60CA8L) /* 661 */,
+ unchecked((long) 0xDCDBC6F3017477D1L) /* 662 */, unchecked((long) 0x4A2E14D4DECAD24DL) /* 663 */,
+ unchecked((long) 0xBDB5E6D9BE0A1EEBL) /* 664 */, unchecked((long) 0x2A7E70F7794301ABL) /* 665 */,
+ unchecked((long) 0xDEF42D8A270540FDL) /* 666 */, unchecked((long) 0x01078EC0A34C22C1L) /* 667 */,
+ unchecked((long) 0xE5DE511AF4C16387L) /* 668 */, unchecked((long) 0x7EBB3A52BD9A330AL) /* 669 */,
+ unchecked((long) 0x77697857AA7D6435L) /* 670 */, unchecked((long) 0x004E831603AE4C32L) /* 671 */,
+ unchecked((long) 0xE7A21020AD78E312L) /* 672 */, unchecked((long) 0x9D41A70C6AB420F2L) /* 673 */,
+ unchecked((long) 0x28E06C18EA1141E6L) /* 674 */, unchecked((long) 0xD2B28CBD984F6B28L) /* 675 */,
+ unchecked((long) 0x26B75F6C446E9D83L) /* 676 */, unchecked((long) 0xBA47568C4D418D7FL) /* 677 */,
+ unchecked((long) 0xD80BADBFE6183D8EL) /* 678 */, unchecked((long) 0x0E206D7F5F166044L) /* 679 */,
+ unchecked((long) 0xE258A43911CBCA3EL) /* 680 */, unchecked((long) 0x723A1746B21DC0BCL) /* 681 */,
+ unchecked((long) 0xC7CAA854F5D7CDD3L) /* 682 */, unchecked((long) 0x7CAC32883D261D9CL) /* 683 */,
+ unchecked((long) 0x7690C26423BA942CL) /* 684 */, unchecked((long) 0x17E55524478042B8L) /* 685 */,
+ unchecked((long) 0xE0BE477656A2389FL) /* 686 */, unchecked((long) 0x4D289B5E67AB2DA0L) /* 687 */,
+ unchecked((long) 0x44862B9C8FBBFD31L) /* 688 */, unchecked((long) 0xB47CC8049D141365L) /* 689 */,
+ unchecked((long) 0x822C1B362B91C793L) /* 690 */, unchecked((long) 0x4EB14655FB13DFD8L) /* 691 */,
+ unchecked((long) 0x1ECBBA0714E2A97BL) /* 692 */, unchecked((long) 0x6143459D5CDE5F14L) /* 693 */,
+ unchecked((long) 0x53A8FBF1D5F0AC89L) /* 694 */, unchecked((long) 0x97EA04D81C5E5B00L) /* 695 */,
+ unchecked((long) 0x622181A8D4FDB3F3L) /* 696 */, unchecked((long) 0xE9BCD341572A1208L) /* 697 */,
+ unchecked((long) 0x1411258643CCE58AL) /* 698 */, unchecked((long) 0x9144C5FEA4C6E0A4L) /* 699 */,
+ unchecked((long) 0x0D33D06565CF620FL) /* 700 */, unchecked((long) 0x54A48D489F219CA1L) /* 701 */,
+ unchecked((long) 0xC43E5EAC6D63C821L) /* 702 */, unchecked((long) 0xA9728B3A72770DAFL) /* 703 */,
+ unchecked((long) 0xD7934E7B20DF87EFL) /* 704 */, unchecked((long) 0xE35503B61A3E86E5L) /* 705 */,
+ unchecked((long) 0xCAE321FBC819D504L) /* 706 */, unchecked((long) 0x129A50B3AC60BFA6L) /* 707 */,
+ unchecked((long) 0xCD5E68EA7E9FB6C3L) /* 708 */, unchecked((long) 0xB01C90199483B1C7L) /* 709 */,
+ unchecked((long) 0x3DE93CD5C295376CL) /* 710 */, unchecked((long) 0xAED52EDF2AB9AD13L) /* 711 */,
+ unchecked((long) 0x2E60F512C0A07884L) /* 712 */, unchecked((long) 0xBC3D86A3E36210C9L) /* 713 */,
+ unchecked((long) 0x35269D9B163951CEL) /* 714 */, unchecked((long) 0x0C7D6E2AD0CDB5FAL) /* 715 */,
+ unchecked((long) 0x59E86297D87F5733L) /* 716 */, unchecked((long) 0x298EF221898DB0E7L) /* 717 */,
+ unchecked((long) 0x55000029D1A5AA7EL) /* 718 */, unchecked((long) 0x8BC08AE1B5061B45L) /* 719 */,
+ unchecked((long) 0xC2C31C2B6C92703AL) /* 720 */, unchecked((long) 0x94CC596BAF25EF42L) /* 721 */,
+ unchecked((long) 0x0A1D73DB22540456L) /* 722 */, unchecked((long) 0x04B6A0F9D9C4179AL) /* 723 */,
+ unchecked((long) 0xEFFDAFA2AE3D3C60L) /* 724 */, unchecked((long) 0xF7C8075BB49496C4L) /* 725 */,
+ unchecked((long) 0x9CC5C7141D1CD4E3L) /* 726 */, unchecked((long) 0x78BD1638218E5534L) /* 727 */,
+ unchecked((long) 0xB2F11568F850246AL) /* 728 */, unchecked((long) 0xEDFABCFA9502BC29L) /* 729 */,
+ unchecked((long) 0x796CE5F2DA23051BL) /* 730 */, unchecked((long) 0xAAE128B0DC93537CL) /* 731 */,
+ unchecked((long) 0x3A493DA0EE4B29AEL) /* 732 */, unchecked((long) 0xB5DF6B2C416895D7L) /* 733 */,
+ unchecked((long) 0xFCABBD25122D7F37L) /* 734 */, unchecked((long) 0x70810B58105DC4B1L) /* 735 */,
+ unchecked((long) 0xE10FDD37F7882A90L) /* 736 */, unchecked((long) 0x524DCAB5518A3F5CL) /* 737 */,
+ unchecked((long) 0x3C9E85878451255BL) /* 738 */, unchecked((long) 0x4029828119BD34E2L) /* 739 */,
+ unchecked((long) 0x74A05B6F5D3CECCBL) /* 740 */, unchecked((long) 0xB610021542E13ECAL) /* 741 */,
+ unchecked((long) 0x0FF979D12F59E2ACL) /* 742 */, unchecked((long) 0x6037DA27E4F9CC50L) /* 743 */,
+ unchecked((long) 0x5E92975A0DF1847DL) /* 744 */, unchecked((long) 0xD66DE190D3E623FEL) /* 745 */,
+ unchecked((long) 0x5032D6B87B568048L) /* 746 */, unchecked((long) 0x9A36B7CE8235216EL) /* 747 */,
+ unchecked((long) 0x80272A7A24F64B4AL) /* 748 */, unchecked((long) 0x93EFED8B8C6916F7L) /* 749 */,
+ unchecked((long) 0x37DDBFF44CCE1555L) /* 750 */, unchecked((long) 0x4B95DB5D4B99BD25L) /* 751 */,
+ unchecked((long) 0x92D3FDA169812FC0L) /* 752 */, unchecked((long) 0xFB1A4A9A90660BB6L) /* 753 */,
+ unchecked((long) 0x730C196946A4B9B2L) /* 754 */, unchecked((long) 0x81E289AA7F49DA68L) /* 755 */,
+ unchecked((long) 0x64669A0F83B1A05FL) /* 756 */, unchecked((long) 0x27B3FF7D9644F48BL) /* 757 */,
+ unchecked((long) 0xCC6B615C8DB675B3L) /* 758 */, unchecked((long) 0x674F20B9BCEBBE95L) /* 759 */,
+ unchecked((long) 0x6F31238275655982L) /* 760 */, unchecked((long) 0x5AE488713E45CF05L) /* 761 */,
+ unchecked((long) 0xBF619F9954C21157L) /* 762 */, unchecked((long) 0xEABAC46040A8EAE9L) /* 763 */,
+ unchecked((long) 0x454C6FE9F2C0C1CDL) /* 764 */, unchecked((long) 0x419CF6496412691CL) /* 765 */,
+ unchecked((long) 0xD3DC3BEF265B0F70L) /* 766 */, unchecked((long) 0x6D0E60F5C3578A9EL) /* 767 */,
+ };
+
+ private static readonly long[] t4 = {
+ unchecked((long) 0x5B0E608526323C55L) /* 768 */, unchecked((long) 0x1A46C1A9FA1B59F5L) /* 769 */,
+ unchecked((long) 0xA9E245A17C4C8FFAL) /* 770 */, unchecked((long) 0x65CA5159DB2955D7L) /* 771 */,
+ unchecked((long) 0x05DB0A76CE35AFC2L) /* 772 */, unchecked((long) 0x81EAC77EA9113D45L) /* 773 */,
+ unchecked((long) 0x528EF88AB6AC0A0DL) /* 774 */, unchecked((long) 0xA09EA253597BE3FFL) /* 775 */,
+ unchecked((long) 0x430DDFB3AC48CD56L) /* 776 */, unchecked((long) 0xC4B3A67AF45CE46FL) /* 777 */,
+ unchecked((long) 0x4ECECFD8FBE2D05EL) /* 778 */, unchecked((long) 0x3EF56F10B39935F0L) /* 779 */,
+ unchecked((long) 0x0B22D6829CD619C6L) /* 780 */, unchecked((long) 0x17FD460A74DF2069L) /* 781 */,
+ unchecked((long) 0x6CF8CC8E8510ED40L) /* 782 */, unchecked((long) 0xD6C824BF3A6ECAA7L) /* 783 */,
+ unchecked((long) 0x61243D581A817049L) /* 784 */, unchecked((long) 0x048BACB6BBC163A2L) /* 785 */,
+ unchecked((long) 0xD9A38AC27D44CC32L) /* 786 */, unchecked((long) 0x7FDDFF5BAAF410ABL) /* 787 */,
+ unchecked((long) 0xAD6D495AA804824BL) /* 788 */, unchecked((long) 0xE1A6A74F2D8C9F94L) /* 789 */,
+ unchecked((long) 0xD4F7851235DEE8E3L) /* 790 */, unchecked((long) 0xFD4B7F886540D893L) /* 791 */,
+ unchecked((long) 0x247C20042AA4BFDAL) /* 792 */, unchecked((long) 0x096EA1C517D1327CL) /* 793 */,
+ unchecked((long) 0xD56966B4361A6685L) /* 794 */, unchecked((long) 0x277DA5C31221057DL) /* 795 */,
+ unchecked((long) 0x94D59893A43ACFF7L) /* 796 */, unchecked((long) 0x64F0C51CCDC02281L) /* 797 */,
+ unchecked((long) 0x3D33BCC4FF6189DBL) /* 798 */, unchecked((long) 0xE005CB184CE66AF1L) /* 799 */,
+ unchecked((long) 0xFF5CCD1D1DB99BEAL) /* 800 */, unchecked((long) 0xB0B854A7FE42980FL) /* 801 */,
+ unchecked((long) 0x7BD46A6A718D4B9FL) /* 802 */, unchecked((long) 0xD10FA8CC22A5FD8CL) /* 803 */,
+ unchecked((long) 0xD31484952BE4BD31L) /* 804 */, unchecked((long) 0xC7FA975FCB243847L) /* 805 */,
+ unchecked((long) 0x4886ED1E5846C407L) /* 806 */, unchecked((long) 0x28CDDB791EB70B04L) /* 807 */,
+ unchecked((long) 0xC2B00BE2F573417FL) /* 808 */, unchecked((long) 0x5C9590452180F877L) /* 809 */,
+ unchecked((long) 0x7A6BDDFFF370EB00L) /* 810 */, unchecked((long) 0xCE509E38D6D9D6A4L) /* 811 */,
+ unchecked((long) 0xEBEB0F00647FA702L) /* 812 */, unchecked((long) 0x1DCC06CF76606F06L) /* 813 */,
+ unchecked((long) 0xE4D9F28BA286FF0AL) /* 814 */, unchecked((long) 0xD85A305DC918C262L) /* 815 */,
+ unchecked((long) 0x475B1D8732225F54L) /* 816 */, unchecked((long) 0x2D4FB51668CCB5FEL) /* 817 */,
+ unchecked((long) 0xA679B9D9D72BBA20L) /* 818 */, unchecked((long) 0x53841C0D912D43A5L) /* 819 */,
+ unchecked((long) 0x3B7EAA48BF12A4E8L) /* 820 */, unchecked((long) 0x781E0E47F22F1DDFL) /* 821 */,
+ unchecked((long) 0xEFF20CE60AB50973L) /* 822 */, unchecked((long) 0x20D261D19DFFB742L) /* 823 */,
+ unchecked((long) 0x16A12B03062A2E39L) /* 824 */, unchecked((long) 0x1960EB2239650495L) /* 825 */,
+ unchecked((long) 0x251C16FED50EB8B8L) /* 826 */, unchecked((long) 0x9AC0C330F826016EL) /* 827 */,
+ unchecked((long) 0xED152665953E7671L) /* 828 */, unchecked((long) 0x02D63194A6369570L) /* 829 */,
+ unchecked((long) 0x5074F08394B1C987L) /* 830 */, unchecked((long) 0x70BA598C90B25CE1L) /* 831 */,
+ unchecked((long) 0x794A15810B9742F6L) /* 832 */, unchecked((long) 0x0D5925E9FCAF8C6CL) /* 833 */,
+ unchecked((long) 0x3067716CD868744EL) /* 834 */, unchecked((long) 0x910AB077E8D7731BL) /* 835 */,
+ unchecked((long) 0x6A61BBDB5AC42F61L) /* 836 */, unchecked((long) 0x93513EFBF0851567L) /* 837 */,
+ unchecked((long) 0xF494724B9E83E9D5L) /* 838 */, unchecked((long) 0xE887E1985C09648DL) /* 839 */,
+ unchecked((long) 0x34B1D3C675370CFDL) /* 840 */, unchecked((long) 0xDC35E433BC0D255DL) /* 841 */,
+ unchecked((long) 0xD0AAB84234131BE0L) /* 842 */, unchecked((long) 0x08042A50B48B7EAFL) /* 843 */,
+ unchecked((long) 0x9997C4EE44A3AB35L) /* 844 */, unchecked((long) 0x829A7B49201799D0L) /* 845 */,
+ unchecked((long) 0x263B8307B7C54441L) /* 846 */, unchecked((long) 0x752F95F4FD6A6CA6L) /* 847 */,
+ unchecked((long) 0x927217402C08C6E5L) /* 848 */, unchecked((long) 0x2A8AB754A795D9EEL) /* 849 */,
+ unchecked((long) 0xA442F7552F72943DL) /* 850 */, unchecked((long) 0x2C31334E19781208L) /* 851 */,
+ unchecked((long) 0x4FA98D7CEAEE6291L) /* 852 */, unchecked((long) 0x55C3862F665DB309L) /* 853 */,
+ unchecked((long) 0xBD0610175D53B1F3L) /* 854 */, unchecked((long) 0x46FE6CB840413F27L) /* 855 */,
+ unchecked((long) 0x3FE03792DF0CFA59L) /* 856 */, unchecked((long) 0xCFE700372EB85E8FL) /* 857 */,
+ unchecked((long) 0xA7BE29E7ADBCE118L) /* 858 */, unchecked((long) 0xE544EE5CDE8431DDL) /* 859 */,
+ unchecked((long) 0x8A781B1B41F1873EL) /* 860 */, unchecked((long) 0xA5C94C78A0D2F0E7L) /* 861 */,
+ unchecked((long) 0x39412E2877B60728L) /* 862 */, unchecked((long) 0xA1265EF3AFC9A62CL) /* 863 */,
+ unchecked((long) 0xBCC2770C6A2506C5L) /* 864 */, unchecked((long) 0x3AB66DD5DCE1CE12L) /* 865 */,
+ unchecked((long) 0xE65499D04A675B37L) /* 866 */, unchecked((long) 0x7D8F523481BFD216L) /* 867 */,
+ unchecked((long) 0x0F6F64FCEC15F389L) /* 868 */, unchecked((long) 0x74EFBE618B5B13C8L) /* 869 */,
+ unchecked((long) 0xACDC82B714273E1DL) /* 870 */, unchecked((long) 0xDD40BFE003199D17L) /* 871 */,
+ unchecked((long) 0x37E99257E7E061F8L) /* 872 */, unchecked((long) 0xFA52626904775AAAL) /* 873 */,
+ unchecked((long) 0x8BBBF63A463D56F9L) /* 874 */, unchecked((long) 0xF0013F1543A26E64L) /* 875 */,
+ unchecked((long) 0xA8307E9F879EC898L) /* 876 */, unchecked((long) 0xCC4C27A4150177CCL) /* 877 */,
+ unchecked((long) 0x1B432F2CCA1D3348L) /* 878 */, unchecked((long) 0xDE1D1F8F9F6FA013L) /* 879 */,
+ unchecked((long) 0x606602A047A7DDD6L) /* 880 */, unchecked((long) 0xD237AB64CC1CB2C7L) /* 881 */,
+ unchecked((long) 0x9B938E7225FCD1D3L) /* 882 */, unchecked((long) 0xEC4E03708E0FF476L) /* 883 */,
+ unchecked((long) 0xFEB2FBDA3D03C12DL) /* 884 */, unchecked((long) 0xAE0BCED2EE43889AL) /* 885 */,
+ unchecked((long) 0x22CB8923EBFB4F43L) /* 886 */, unchecked((long) 0x69360D013CF7396DL) /* 887 */,
+ unchecked((long) 0x855E3602D2D4E022L) /* 888 */, unchecked((long) 0x073805BAD01F784CL) /* 889 */,
+ unchecked((long) 0x33E17A133852F546L) /* 890 */, unchecked((long) 0xDF4874058AC7B638L) /* 891 */,
+ unchecked((long) 0xBA92B29C678AA14AL) /* 892 */, unchecked((long) 0x0CE89FC76CFAADCDL) /* 893 */,
+ unchecked((long) 0x5F9D4E0908339E34L) /* 894 */, unchecked((long) 0xF1AFE9291F5923B9L) /* 895 */,
+ unchecked((long) 0x6E3480F60F4A265FL) /* 896 */, unchecked((long) 0xEEBF3A2AB29B841CL) /* 897 */,
+ unchecked((long) 0xE21938A88F91B4ADL) /* 898 */, unchecked((long) 0x57DFEFF845C6D3C3L) /* 899 */,
+ unchecked((long) 0x2F006B0BF62CAAF2L) /* 900 */, unchecked((long) 0x62F479EF6F75EE78L) /* 901 */,
+ unchecked((long) 0x11A55AD41C8916A9L) /* 902 */, unchecked((long) 0xF229D29084FED453L) /* 903 */,
+ unchecked((long) 0x42F1C27B16B000E6L) /* 904 */, unchecked((long) 0x2B1F76749823C074L) /* 905 */,
+ unchecked((long) 0x4B76ECA3C2745360L) /* 906 */, unchecked((long) 0x8C98F463B91691BDL) /* 907 */,
+ unchecked((long) 0x14BCC93CF1ADE66AL) /* 908 */, unchecked((long) 0x8885213E6D458397L) /* 909 */,
+ unchecked((long) 0x8E177DF0274D4711L) /* 910 */, unchecked((long) 0xB49B73B5503F2951L) /* 911 */,
+ unchecked((long) 0x10168168C3F96B6BL) /* 912 */, unchecked((long) 0x0E3D963B63CAB0AEL) /* 913 */,
+ unchecked((long) 0x8DFC4B5655A1DB14L) /* 914 */, unchecked((long) 0xF789F1356E14DE5CL) /* 915 */,
+ unchecked((long) 0x683E68AF4E51DAC1L) /* 916 */, unchecked((long) 0xC9A84F9D8D4B0FD9L) /* 917 */,
+ unchecked((long) 0x3691E03F52A0F9D1L) /* 918 */, unchecked((long) 0x5ED86E46E1878E80L) /* 919 */,
+ unchecked((long) 0x3C711A0E99D07150L) /* 920 */, unchecked((long) 0x5A0865B20C4E9310L) /* 921 */,
+ unchecked((long) 0x56FBFC1FE4F0682EL) /* 922 */, unchecked((long) 0xEA8D5DE3105EDF9BL) /* 923 */,
+ unchecked((long) 0x71ABFDB12379187AL) /* 924 */, unchecked((long) 0x2EB99DE1BEE77B9CL) /* 925 */,
+ unchecked((long) 0x21ECC0EA33CF4523L) /* 926 */, unchecked((long) 0x59A4D7521805C7A1L) /* 927 */,
+ unchecked((long) 0x3896F5EB56AE7C72L) /* 928 */, unchecked((long) 0xAA638F3DB18F75DCL) /* 929 */,
+ unchecked((long) 0x9F39358DABE9808EL) /* 930 */, unchecked((long) 0xB7DEFA91C00B72ACL) /* 931 */,
+ unchecked((long) 0x6B5541FD62492D92L) /* 932 */, unchecked((long) 0x6DC6DEE8F92E4D5BL) /* 933 */,
+ unchecked((long) 0x353F57ABC4BEEA7EL) /* 934 */, unchecked((long) 0x735769D6DA5690CEL) /* 935 */,
+ unchecked((long) 0x0A234AA642391484L) /* 936 */, unchecked((long) 0xF6F9508028F80D9DL) /* 937 */,
+ unchecked((long) 0xB8E319A27AB3F215L) /* 938 */, unchecked((long) 0x31AD9C1151341A4DL) /* 939 */,
+ unchecked((long) 0x773C22A57BEF5805L) /* 940 */, unchecked((long) 0x45C7561A07968633L) /* 941 */,
+ unchecked((long) 0xF913DA9E249DBE36L) /* 942 */, unchecked((long) 0xDA652D9B78A64C68L) /* 943 */,
+ unchecked((long) 0x4C27A97F3BC334EFL) /* 944 */, unchecked((long) 0x76621220E66B17F4L) /* 945 */,
+ unchecked((long) 0x967743899ACD7D0BL) /* 946 */, unchecked((long) 0xF3EE5BCAE0ED6782L) /* 947 */,
+ unchecked((long) 0x409F753600C879FCL) /* 948 */, unchecked((long) 0x06D09A39B5926DB6L) /* 949 */,
+ unchecked((long) 0x6F83AEB0317AC588L) /* 950 */, unchecked((long) 0x01E6CA4A86381F21L) /* 951 */,
+ unchecked((long) 0x66FF3462D19F3025L) /* 952 */, unchecked((long) 0x72207C24DDFD3BFBL) /* 953 */,
+ unchecked((long) 0x4AF6B6D3E2ECE2EBL) /* 954 */, unchecked((long) 0x9C994DBEC7EA08DEL) /* 955 */,
+ unchecked((long) 0x49ACE597B09A8BC4L) /* 956 */, unchecked((long) 0xB38C4766CF0797BAL) /* 957 */,
+ unchecked((long) 0x131B9373C57C2A75L) /* 958 */, unchecked((long) 0xB1822CCE61931E58L) /* 959 */,
+ unchecked((long) 0x9D7555B909BA1C0CL) /* 960 */, unchecked((long) 0x127FAFDD937D11D2L) /* 961 */,
+ unchecked((long) 0x29DA3BADC66D92E4L) /* 962 */, unchecked((long) 0xA2C1D57154C2ECBCL) /* 963 */,
+ unchecked((long) 0x58C5134D82F6FE24L) /* 964 */, unchecked((long) 0x1C3AE3515B62274FL) /* 965 */,
+ unchecked((long) 0xE907C82E01CB8126L) /* 966 */, unchecked((long) 0xF8ED091913E37FCBL) /* 967 */,
+ unchecked((long) 0x3249D8F9C80046C9L) /* 968 */, unchecked((long) 0x80CF9BEDE388FB63L) /* 969 */,
+ unchecked((long) 0x1881539A116CF19EL) /* 970 */, unchecked((long) 0x5103F3F76BD52457L) /* 971 */,
+ unchecked((long) 0x15B7E6F5AE47F7A8L) /* 972 */, unchecked((long) 0xDBD7C6DED47E9CCFL) /* 973 */,
+ unchecked((long) 0x44E55C410228BB1AL) /* 974 */, unchecked((long) 0xB647D4255EDB4E99L) /* 975 */,
+ unchecked((long) 0x5D11882BB8AAFC30L) /* 976 */, unchecked((long) 0xF5098BBB29D3212AL) /* 977 */,
+ unchecked((long) 0x8FB5EA14E90296B3L) /* 978 */, unchecked((long) 0x677B942157DD025AL) /* 979 */,
+ unchecked((long) 0xFB58E7C0A390ACB5L) /* 980 */, unchecked((long) 0x89D3674C83BD4A01L) /* 981 */,
+ unchecked((long) 0x9E2DA4DF4BF3B93BL) /* 982 */, unchecked((long) 0xFCC41E328CAB4829L) /* 983 */,
+ unchecked((long) 0x03F38C96BA582C52L) /* 984 */, unchecked((long) 0xCAD1BDBD7FD85DB2L) /* 985 */,
+ unchecked((long) 0xBBB442C16082AE83L) /* 986 */, unchecked((long) 0xB95FE86BA5DA9AB0L) /* 987 */,
+ unchecked((long) 0xB22E04673771A93FL) /* 988 */, unchecked((long) 0x845358C9493152D8L) /* 989 */,
+ unchecked((long) 0xBE2A488697B4541EL) /* 990 */, unchecked((long) 0x95A2DC2DD38E6966L) /* 991 */,
+ unchecked((long) 0xC02C11AC923C852BL) /* 992 */, unchecked((long) 0x2388B1990DF2A87BL) /* 993 */,
+ unchecked((long) 0x7C8008FA1B4F37BEL) /* 994 */, unchecked((long) 0x1F70D0C84D54E503L) /* 995 */,
+ unchecked((long) 0x5490ADEC7ECE57D4L) /* 996 */, unchecked((long) 0x002B3C27D9063A3AL) /* 997 */,
+ unchecked((long) 0x7EAEA3848030A2BFL) /* 998 */, unchecked((long) 0xC602326DED2003C0L) /* 999 */,
+ unchecked((long) 0x83A7287D69A94086L) /* 1000 */, unchecked((long) 0xC57A5FCB30F57A8AL) /* 1001 */,
+ unchecked((long) 0xB56844E479EBE779L) /* 1002 */, unchecked((long) 0xA373B40F05DCBCE9L) /* 1003 */,
+ unchecked((long) 0xD71A786E88570EE2L) /* 1004 */, unchecked((long) 0x879CBACDBDE8F6A0L) /* 1005 */,
+ unchecked((long) 0x976AD1BCC164A32FL) /* 1006 */, unchecked((long) 0xAB21E25E9666D78BL) /* 1007 */,
+ unchecked((long) 0x901063AAE5E5C33CL) /* 1008 */, unchecked((long) 0x9818B34448698D90L) /* 1009 */,
+ unchecked((long) 0xE36487AE3E1E8ABBL) /* 1010 */, unchecked((long) 0xAFBDF931893BDCB4L) /* 1011 */,
+ unchecked((long) 0x6345A0DC5FBBD519L) /* 1012 */, unchecked((long) 0x8628FE269B9465CAL) /* 1013 */,
+ unchecked((long) 0x1E5D01603F9C51ECL) /* 1014 */, unchecked((long) 0x4DE44006A15049B7L) /* 1015 */,
+ unchecked((long) 0xBF6C70E5F776CBB1L) /* 1016 */, unchecked((long) 0x411218F2EF552BEDL) /* 1017 */,
+ unchecked((long) 0xCB0C0708705A36A3L) /* 1018 */, unchecked((long) 0xE74D14754F986044L) /* 1019 */,
+ unchecked((long) 0xCD56D9430EA8280EL) /* 1020 */, unchecked((long) 0xC12591D7535F5065L) /* 1021 */,
+ unchecked((long) 0xC83223F1720AEF96L) /* 1022 */, unchecked((long) 0xC3A0396F7363A51FL) /* 1023 */
+ };
+
+ private const int DigestLength = 24;
+
+ //
+ // registers
+ //
+ private long a, b, c;
+ private long byteCount;
+
+ //
+ // buffers
+ //
+ private byte[] Buffer = new byte[8];
+ private int bOff;
+
+ private long[] x = new long[8];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public TigerDigest()
+ {
+ Reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public TigerDigest(TigerDigest t)
+ {
+ a = t.a;
+ b = t.b;
+ c = t.c;
+
+ Array.Copy(t.x, 0, x, 0, t.x.Length);
+ xOff = t.xOff;
+
+ Array.Copy(t.Buffer, 0, Buffer, 0, t.Buffer.Length);
+ bOff = t.bOff;
+
+ byteCount = t.byteCount;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Tiger"; }
+ }
+
+ public int GetDigestSize()
+ {
+ return DigestLength;
+ }
+
+ public int GetByteLength()
+ {
+ return MyByteLength;
+ }
+
+ private void ProcessWord(
+ byte[] b,
+ int off)
+ {
+ x[xOff++] = ((long)(b[off + 7] & 0xff) << 56)
+ | ((long)(b[off + 6] & 0xff) << 48)
+ | ((long)(b[off + 5] & 0xff) << 40)
+ | ((long)(b[off + 4] & 0xff) << 32)
+ | ((long)(b[off + 3] & 0xff) << 24)
+ | ((long)(b[off + 2] & 0xff) << 16)
+ | ((long)(b[off + 1] & 0xff) << 8)
+ | ((uint)(b[off + 0] & 0xff));
+
+ if (xOff == x.Length)
+ {
+ ProcessBlock();
+ }
+
+ bOff = 0;
+ }
+
+ public void Update(
+ byte input)
+ {
+ Buffer[bOff++] = input;
+
+ if (bOff == Buffer.Length)
+ {
+ ProcessWord(Buffer, 0);
+ }
+
+ byteCount++;
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ //
+ // fill the current word
+ //
+ while ((bOff != 0) && (length > 0))
+ {
+ Update(input[inOff]);
+
+ inOff++;
+ length--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (length > 8)
+ {
+ ProcessWord(input, inOff);
+
+ inOff += 8;
+ length -= 8;
+ byteCount += 8;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (length > 0)
+ {
+ Update(input[inOff]);
+
+ inOff++;
+ length--;
+ }
+ }
+
+ private void RoundABC(
+ long x,
+ long mul)
+ {
+ c ^= x ;
+ a -= t1[(int)c & 0xff] ^ t2[(int)(c >> 16) & 0xff]
+ ^ t3[(int)(c >> 32) & 0xff] ^ t4[(int)(c >> 48) & 0xff];
+ b += t4[(int)(c >> 8) & 0xff] ^ t3[(int)(c >> 24) & 0xff]
+ ^ t2[(int)(c >> 40) & 0xff] ^ t1[(int)(c >> 56) & 0xff];
+ b *= mul;
+ }
+
+ private void RoundBCA(
+ long x,
+ long mul)
+ {
+ a ^= x ;
+ b -= t1[(int)a & 0xff] ^ t2[(int)(a >> 16) & 0xff]
+ ^ t3[(int)(a >> 32) & 0xff] ^ t4[(int)(a >> 48) & 0xff];
+ c += t4[(int)(a >> 8) & 0xff] ^ t3[(int)(a >> 24) & 0xff]
+ ^ t2[(int)(a >> 40) & 0xff] ^ t1[(int)(a >> 56) & 0xff];
+ c *= mul;
+ }
+
+ private void RoundCAB(
+ long x,
+ long mul)
+ {
+ b ^= x ;
+ c -= t1[(int)b & 0xff] ^ t2[(int)(b >> 16) & 0xff]
+ ^ t3[(int)(b >> 32) & 0xff] ^ t4[(int)(b >> 48) & 0xff];
+ a += t4[(int)(b >> 8) & 0xff] ^ t3[(int)(b >> 24) & 0xff]
+ ^ t2[(int)(b >> 40) & 0xff] ^ t1[(int)(b >> 56) & 0xff];
+ a *= mul;
+ }
+
+ private void KeySchedule()
+ {
+ x[0] -= x[7] ^ unchecked ((long) 0xA5A5A5A5A5A5A5A5L);
+ x[1] ^= x[0];
+ x[2] += x[1];
+ x[3] -= x[2] ^ ((~x[1]) << 19);
+ x[4] ^= x[3];
+ x[5] += x[4];
+ x[6] -= x[5] ^ (long) ((ulong) (~x[4]) >> 23);
+ x[7] ^= x[6];
+ x[0] += x[7];
+ x[1] -= x[0] ^ ((~x[7]) << 19);
+ x[2] ^= x[1];
+ x[3] += x[2];
+ x[4] -= x[3] ^ (long) ((ulong) (~x[2]) >> 23);
+ x[5] ^= x[4];
+ x[6] += x[5];
+ x[7] -= x[6] ^ 0x0123456789ABCDEFL;
+ }
+
+ private void ProcessBlock()
+ {
+ //
+ // save abc
+ //
+ long aa = a;
+ long bb = b;
+ long cc = c;
+
+ //
+ // rounds and schedule
+ //
+ RoundABC(x[0], 5);
+ RoundBCA(x[1], 5);
+ RoundCAB(x[2], 5);
+ RoundABC(x[3], 5);
+ RoundBCA(x[4], 5);
+ RoundCAB(x[5], 5);
+ RoundABC(x[6], 5);
+ RoundBCA(x[7], 5);
+
+ KeySchedule();
+
+ RoundCAB(x[0], 7);
+ RoundABC(x[1], 7);
+ RoundBCA(x[2], 7);
+ RoundCAB(x[3], 7);
+ RoundABC(x[4], 7);
+ RoundBCA(x[5], 7);
+ RoundCAB(x[6], 7);
+ RoundABC(x[7], 7);
+
+ KeySchedule();
+
+ RoundBCA(x[0], 9);
+ RoundCAB(x[1], 9);
+ RoundABC(x[2], 9);
+ RoundBCA(x[3], 9);
+ RoundCAB(x[4], 9);
+ RoundABC(x[5], 9);
+ RoundBCA(x[6], 9);
+ RoundCAB(x[7], 9);
+
+ //
+ // feed forward
+ //
+ a ^= aa;
+ b -= bb;
+ c += cc;
+
+ //
+ // clear the x buffer
+ //
+ xOff = 0;
+ for (int i = 0; i != x.Length; i++)
+ {
+ x[i] = 0;
+ }
+ }
+
+ private void UnpackWord(
+ long r,
+ byte[] output,
+ int outOff)
+ {
+ output[outOff + 7] = (byte)(r >> 56);
+ output[outOff + 6] = (byte)(r >> 48);
+ output[outOff + 5] = (byte)(r >> 40);
+ output[outOff + 4] = (byte)(r >> 32);
+ output[outOff + 3] = (byte)(r >> 24);
+ output[outOff + 2] = (byte)(r >> 16);
+ output[outOff + 1] = (byte)(r >> 8);
+ output[outOff] = (byte)r;
+ }
+
+ private void ProcessLength(
+ long bitLength)
+ {
+ x[7] = bitLength;
+ }
+
+ private void Finish()
+ {
+ long bitLength = (byteCount << 3);
+
+ Update((byte)0x01);
+
+ while (bOff != 0)
+ {
+ Update((byte)0);
+ }
+
+ ProcessLength(bitLength);
+
+ ProcessBlock();
+ }
+
+ public int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ Finish();
+
+ UnpackWord(a, output, outOff);
+ UnpackWord(b, output, outOff + 8);
+ UnpackWord(c, output, outOff + 16);
+
+ Reset();
+
+ return DigestLength;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void Reset()
+ {
+ a = unchecked((long) 0x0123456789ABCDEFL);
+ b = unchecked((long) 0xFEDCBA9876543210L);
+ c = unchecked((long) 0xF096A5B4C3B2E187L);
+
+ xOff = 0;
+ for (int i = 0; i != x.Length; i++)
+ {
+ x[i] = 0;
+ }
+
+ bOff = 0;
+ for (int i = 0; i != Buffer.Length; i++)
+ {
+ Buffer[i] = 0;
+ }
+
+ byteCount = 0;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/digests/WhirlpoolDigest.cs b/Crypto/src/crypto/digests/WhirlpoolDigest.cs
new file mode 100644
index 000000000..df83f4508
--- /dev/null
+++ b/Crypto/src/crypto/digests/WhirlpoolDigest.cs
@@ -0,0 +1,397 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /**
+ * Implementation of WhirlpoolDigest, based on Java source published by Barreto
+ * and Rijmen.
+ *
+ */
+ public sealed class WhirlpoolDigest : IDigest
+ {
+ private const int BYTE_LENGTH = 64;
+
+ private const int DIGEST_LENGTH_BYTES = 512 / 8;
+ private const int ROUNDS = 10;
+ private const int REDUCTION_POLYNOMIAL = 0x011d; // 2^8 + 2^4 + 2^3 + 2 + 1;
+
+ private static readonly int[] SBOX =
+ {
+ 0x18, 0x23, 0xc6, 0xe8, 0x87, 0xb8, 0x01, 0x4f, 0x36, 0xa6, 0xd2, 0xf5, 0x79, 0x6f, 0x91, 0x52,
+ 0x60, 0xbc, 0x9b, 0x8e, 0xa3, 0x0c, 0x7b, 0x35, 0x1d, 0xe0, 0xd7, 0xc2, 0x2e, 0x4b, 0xfe, 0x57,
+ 0x15, 0x77, 0x37, 0xe5, 0x9f, 0xf0, 0x4a, 0xda, 0x58, 0xc9, 0x29, 0x0a, 0xb1, 0xa0, 0x6b, 0x85,
+ 0xbd, 0x5d, 0x10, 0xf4, 0xcb, 0x3e, 0x05, 0x67, 0xe4, 0x27, 0x41, 0x8b, 0xa7, 0x7d, 0x95, 0xd8,
+ 0xfb, 0xee, 0x7c, 0x66, 0xdd, 0x17, 0x47, 0x9e, 0xca, 0x2d, 0xbf, 0x07, 0xad, 0x5a, 0x83, 0x33,
+ 0x63, 0x02, 0xaa, 0x71, 0xc8, 0x19, 0x49, 0xd9, 0xf2, 0xe3, 0x5b, 0x88, 0x9a, 0x26, 0x32, 0xb0,
+ 0xe9, 0x0f, 0xd5, 0x80, 0xbe, 0xcd, 0x34, 0x48, 0xff, 0x7a, 0x90, 0x5f, 0x20, 0x68, 0x1a, 0xae,
+ 0xb4, 0x54, 0x93, 0x22, 0x64, 0xf1, 0x73, 0x12, 0x40, 0x08, 0xc3, 0xec, 0xdb, 0xa1, 0x8d, 0x3d,
+ 0x97, 0x00, 0xcf, 0x2b, 0x76, 0x82, 0xd6, 0x1b, 0xb5, 0xaf, 0x6a, 0x50, 0x45, 0xf3, 0x30, 0xef,
+ 0x3f, 0x55, 0xa2, 0xea, 0x65, 0xba, 0x2f, 0xc0, 0xde, 0x1c, 0xfd, 0x4d, 0x92, 0x75, 0x06, 0x8a,
+ 0xb2, 0xe6, 0x0e, 0x1f, 0x62, 0xd4, 0xa8, 0x96, 0xf9, 0xc5, 0x25, 0x59, 0x84, 0x72, 0x39, 0x4c,
+ 0x5e, 0x78, 0x38, 0x8c, 0xd1, 0xa5, 0xe2, 0x61, 0xb3, 0x21, 0x9c, 0x1e, 0x43, 0xc7, 0xfc, 0x04,
+ 0x51, 0x99, 0x6d, 0x0d, 0xfa, 0xdf, 0x7e, 0x24, 0x3b, 0xab, 0xce, 0x11, 0x8f, 0x4e, 0xb7, 0xeb,
+ 0x3c, 0x81, 0x94, 0xf7, 0xb9, 0x13, 0x2c, 0xd3, 0xe7, 0x6e, 0xc4, 0x03, 0x56, 0x44, 0x7f, 0xa9,
+ 0x2a, 0xbb, 0xc1, 0x53, 0xdc, 0x0b, 0x9d, 0x6c, 0x31, 0x74, 0xf6, 0x46, 0xac, 0x89, 0x14, 0xe1,
+ 0x16, 0x3a, 0x69, 0x09, 0x70, 0xb6, 0xd0, 0xed, 0xcc, 0x42, 0x98, 0xa4, 0x28, 0x5c, 0xf8, 0x86
+ };
+
+ private static readonly long[] C0 = new long[256];
+ private static readonly long[] C1 = new long[256];
+ private static readonly long[] C2 = new long[256];
+ private static readonly long[] C3 = new long[256];
+ private static readonly long[] C4 = new long[256];
+ private static readonly long[] C5 = new long[256];
+ private static readonly long[] C6 = new long[256];
+ private static readonly long[] C7 = new long[256];
+
+ private readonly long[] _rc = new long[ROUNDS + 1];
+
+ /*
+ * increment() can be implemented in this way using 2 arrays or
+ * by having some temporary variables that are used to set the
+ * value provided by EIGHT[i] and carry within the loop.
+ *
+ * not having done any timing, this seems likely to be faster
+ * at the slight expense of 32*(sizeof short) bytes
+ */
+ private static readonly short[] EIGHT = new short[BITCOUNT_ARRAY_SIZE];
+
+ static WhirlpoolDigest()
+ {
+ EIGHT[BITCOUNT_ARRAY_SIZE - 1] = 8;
+
+ for (int i = 0; i < 256; i++)
+ {
+ int v1 = SBOX[i];
+ int v2 = maskWithReductionPolynomial(v1 << 1);
+ int v4 = maskWithReductionPolynomial(v2 << 1);
+ int v5 = v4 ^ v1;
+ int v8 = maskWithReductionPolynomial(v4 << 1);
+ int v9 = v8 ^ v1;
+
+ C0[i] = packIntoLong(v1, v1, v4, v1, v8, v5, v2, v9);
+ C1[i] = packIntoLong(v9, v1, v1, v4, v1, v8, v5, v2);
+ C2[i] = packIntoLong(v2, v9, v1, v1, v4, v1, v8, v5);
+ C3[i] = packIntoLong(v5, v2, v9, v1, v1, v4, v1, v8);
+ C4[i] = packIntoLong(v8, v5, v2, v9, v1, v1, v4, v1);
+ C5[i] = packIntoLong(v1, v8, v5, v2, v9, v1, v1, v4);
+ C6[i] = packIntoLong(v4, v1, v8, v5, v2, v9, v1, v1);
+ C7[i] = packIntoLong(v1, v4, v1, v8, v5, v2, v9, v1);
+ }
+ }
+
+ public WhirlpoolDigest()
+ {
+ _rc[0] = 0L;
+ for (int r = 1; r <= ROUNDS; r++)
+ {
+ int i = 8 * (r - 1);
+ _rc[r] = (long)((ulong)C0[i] & 0xff00000000000000L) ^
+ (C1[i + 1] & (long) 0x00ff000000000000L) ^
+ (C2[i + 2] & (long) 0x0000ff0000000000L) ^
+ (C3[i + 3] & (long) 0x000000ff00000000L) ^
+ (C4[i + 4] & (long) 0x00000000ff000000L) ^
+ (C5[i + 5] & (long) 0x0000000000ff0000L) ^
+ (C6[i + 6] & (long) 0x000000000000ff00L) ^
+ (C7[i + 7] & (long) 0x00000000000000ffL);
+ }
+ }
+
+ private static long packIntoLong(int b7, int b6, int b5, int b4, int b3, int b2, int b1, int b0)
+ {
+ return
+ ((long)b7 << 56) ^
+ ((long)b6 << 48) ^
+ ((long)b5 << 40) ^
+ ((long)b4 << 32) ^
+ ((long)b3 << 24) ^
+ ((long)b2 << 16) ^
+ ((long)b1 << 8) ^
+ b0;
+ }
+
+ /*
+ * int's are used to prevent sign extension. The values that are really being used are
+ * actually just 0..255
+ */
+ private static int maskWithReductionPolynomial(int input)
+ {
+ int rv = input;
+ if (rv >= 0x100L) // high bit set
+ {
+ rv ^= REDUCTION_POLYNOMIAL; // reduced by the polynomial
+ }
+ return rv;
+ }
+
+ // --------------------------------------------------------------------------------------//
+
+ // -- buffer information --
+ private const int BITCOUNT_ARRAY_SIZE = 32;
+ private byte[] _buffer = new byte[64];
+ private int _bufferPos;
+ private short[] _bitCount = new short[BITCOUNT_ARRAY_SIZE];
+
+ // -- internal hash state --
+ private long[] _hash = new long[8];
+ private long[] _K = new long[8]; // the round key
+ private long[] _L = new long[8];
+ private long[] _block = new long[8]; // mu (buffer)
+ private long[] _state = new long[8]; // the current "cipher" state
+
+
+
+ /**
+ * Copy constructor. This will copy the state of the provided message
+ * digest.
+ */
+ public WhirlpoolDigest(WhirlpoolDigest originalDigest)
+ {
+ Array.Copy(originalDigest._rc, 0, _rc, 0, _rc.Length);
+
+ Array.Copy(originalDigest._buffer, 0, _buffer, 0, _buffer.Length);
+
+ this._bufferPos = originalDigest._bufferPos;
+ Array.Copy(originalDigest._bitCount, 0, _bitCount, 0, _bitCount.Length);
+
+ // -- internal hash state --
+ Array.Copy(originalDigest._hash, 0, _hash, 0, _hash.Length);
+ Array.Copy(originalDigest._K, 0, _K, 0, _K.Length);
+ Array.Copy(originalDigest._L, 0, _L, 0, _L.Length);
+ Array.Copy(originalDigest._block, 0, _block, 0, _block.Length);
+ Array.Copy(originalDigest._state, 0, _state, 0, _state.Length);
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Whirlpool"; }
+ }
+
+ public int GetDigestSize()
+ {
+ return DIGEST_LENGTH_BYTES;
+ }
+
+ public int DoFinal(byte[] output, int outOff)
+ {
+ // sets output[outOff] .. output[outOff+DIGEST_LENGTH_BYTES]
+ finish();
+
+ for (int i = 0; i < 8; i++)
+ {
+ convertLongToByteArray(_hash[i], output, outOff + (i * 8));
+ }
+
+ Reset();
+
+ return GetDigestSize();
+ }
+
+ /**
+ * Reset the chaining variables
+ */
+ public void Reset()
+ {
+ // set variables to null, blank, whatever
+ _bufferPos = 0;
+ Array.Clear(_bitCount, 0, _bitCount.Length);
+ Array.Clear(_buffer, 0, _buffer.Length);
+ Array.Clear(_hash, 0, _hash.Length);
+ Array.Clear(_K, 0, _K.Length);
+ Array.Clear(_L, 0, _L.Length);
+ Array.Clear(_block, 0, _block.Length);
+ Array.Clear(_state, 0, _state.Length);
+ }
+
+ // this takes a buffer of information and fills the block
+ private void processFilledBuffer()
+ {
+ // copies into the block...
+ for (int i = 0; i < _state.Length; i++)
+ {
+ _block[i] = bytesToLongFromBuffer(_buffer, i * 8);
+ }
+ processBlock();
+ _bufferPos = 0;
+ Array.Clear(_buffer, 0, _buffer.Length);
+ }
+
+ private static long bytesToLongFromBuffer(byte[] buffer, int startPos)
+ {
+ long rv = (((buffer[startPos + 0] & 0xffL) << 56) |
+ ((buffer[startPos + 1] & 0xffL) << 48) |
+ ((buffer[startPos + 2] & 0xffL) << 40) |
+ ((buffer[startPos + 3] & 0xffL) << 32) |
+ ((buffer[startPos + 4] & 0xffL) << 24) |
+ ((buffer[startPos + 5] & 0xffL) << 16) |
+ ((buffer[startPos + 6] & 0xffL) << 8) |
+ ((buffer[startPos + 7]) & 0xffL));
+
+ return rv;
+ }
+
+ private static void convertLongToByteArray(long inputLong, byte[] outputArray, int offSet)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ outputArray[offSet + i] = (byte)((inputLong >> (56 - (i * 8))) & 0xff);
+ }
+ }
+
+ private void processBlock()
+ {
+ // buffer contents have been transferred to the _block[] array via
+ // processFilledBuffer
+
+ // compute and apply K^0
+ for (int i = 0; i < 8; i++)
+ {
+ _state[i] = _block[i] ^ (_K[i] = _hash[i]);
+ }
+
+ // iterate over the rounds
+ for (int round = 1; round <= ROUNDS; round++)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ _L[i] = 0;
+ _L[i] ^= C0[(int)(_K[(i - 0) & 7] >> 56) & 0xff];
+ _L[i] ^= C1[(int)(_K[(i - 1) & 7] >> 48) & 0xff];
+ _L[i] ^= C2[(int)(_K[(i - 2) & 7] >> 40) & 0xff];
+ _L[i] ^= C3[(int)(_K[(i - 3) & 7] >> 32) & 0xff];
+ _L[i] ^= C4[(int)(_K[(i - 4) & 7] >> 24) & 0xff];
+ _L[i] ^= C5[(int)(_K[(i - 5) & 7] >> 16) & 0xff];
+ _L[i] ^= C6[(int)(_K[(i - 6) & 7] >> 8) & 0xff];
+ _L[i] ^= C7[(int)(_K[(i - 7) & 7]) & 0xff];
+ }
+
+ Array.Copy(_L, 0, _K, 0, _K.Length);
+
+ _K[0] ^= _rc[round];
+
+ // apply the round transformation
+ for (int i = 0; i < 8; i++)
+ {
+ _L[i] = _K[i];
+
+ _L[i] ^= C0[(int)(_state[(i - 0) & 7] >> 56) & 0xff];
+ _L[i] ^= C1[(int)(_state[(i - 1) & 7] >> 48) & 0xff];
+ _L[i] ^= C2[(int)(_state[(i - 2) & 7] >> 40) & 0xff];
+ _L[i] ^= C3[(int)(_state[(i - 3) & 7] >> 32) & 0xff];
+ _L[i] ^= C4[(int)(_state[(i - 4) & 7] >> 24) & 0xff];
+ _L[i] ^= C5[(int)(_state[(i - 5) & 7] >> 16) & 0xff];
+ _L[i] ^= C6[(int)(_state[(i - 6) & 7] >> 8) & 0xff];
+ _L[i] ^= C7[(int)(_state[(i - 7) & 7]) & 0xff];
+ }
+
+ // save the current state
+ Array.Copy(_L, 0, _state, 0, _state.Length);
+ }
+
+ // apply Miuaguchi-Preneel compression
+ for (int i = 0; i < 8; i++)
+ {
+ _hash[i] ^= _state[i] ^ _block[i];
+ }
+
+ }
+
+ public void Update(byte input)
+ {
+ _buffer[_bufferPos] = input;
+
+ //Console.WriteLine("adding to buffer = "+_buffer[_bufferPos]);
+
+ ++_bufferPos;
+
+ if (_bufferPos == _buffer.Length)
+ {
+ processFilledBuffer();
+ }
+
+ increment();
+ }
+
+ private void increment()
+ {
+ int carry = 0;
+ for (int i = _bitCount.Length - 1; i >= 0; i--)
+ {
+ int sum = (_bitCount[i] & 0xff) + EIGHT[i] + carry;
+
+ carry = sum >> 8;
+ _bitCount[i] = (short)(sum & 0xff);
+ }
+ }
+
+ public void BlockUpdate(byte[] input, int inOff, int length)
+ {
+ while (length > 0)
+ {
+ Update(input[inOff]);
+ ++inOff;
+ --length;
+ }
+
+ }
+
+ private void finish()
+ {
+ /*
+ * this makes a copy of the current bit length. at the expense of an
+ * object creation of 32 bytes rather than providing a _stopCounting
+ * boolean which was the alternative I could think of.
+ */
+ byte[] bitLength = copyBitLength();
+
+ _buffer[_bufferPos++] |= 0x80;
+
+ if (_bufferPos == _buffer.Length)
+ {
+ processFilledBuffer();
+ }
+
+ /*
+ * Final block contains
+ * [ ... data .... ][0][0][0][ length ]
+ *
+ * if [ length ] cannot fit. Need to create a new block.
+ */
+ if (_bufferPos > 32)
+ {
+ while (_bufferPos != 0)
+ {
+ Update((byte)0);
+ }
+ }
+
+ while (_bufferPos <= 32)
+ {
+ Update((byte)0);
+ }
+
+ // copy the length information to the final 32 bytes of the
+ // 64 byte block....
+ Array.Copy(bitLength, 0, _buffer, 32, bitLength.Length);
+
+ processFilledBuffer();
+ }
+
+ private byte[] copyBitLength()
+ {
+ byte[] rv = new byte[BITCOUNT_ARRAY_SIZE];
+ for (int i = 0; i < rv.Length; i++)
+ {
+ rv[i] = (byte)(_bitCount[i] & 0xff);
+ }
+ return rv;
+ }
+
+ public int GetByteLength()
+ {
+ return BYTE_LENGTH;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/encodings/ISO9796d1Encoding.cs b/Crypto/src/crypto/encodings/ISO9796d1Encoding.cs
new file mode 100644
index 000000000..30e988356
--- /dev/null
+++ b/Crypto/src/crypto/encodings/ISO9796d1Encoding.cs
@@ -0,0 +1,273 @@
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Encodings
+{
+ /**
+ * ISO 9796-1 padding. Note in the light of recent results you should
+ * only use this with RSA (rather than the "simpler" Rabin keys) and you
+ * should never use it with anything other than a hash (ie. even if the
+ * message is small don't sign the message, sign it's hash) or some "random"
+ * value. See your favorite search engine for details.
+ */
+ public class ISO9796d1Encoding
+ : IAsymmetricBlockCipher
+ {
+ private static readonly BigInteger Sixteen = BigInteger.ValueOf(16);
+ private static readonly BigInteger Six = BigInteger.ValueOf(6);
+
+ private static readonly byte[] shadows = { 0xe, 0x3, 0x5, 0x8, 0x9, 0x4, 0x2, 0xf,
+ 0x0, 0xd, 0xb, 0x6, 0x7, 0xa, 0xc, 0x1 };
+ private static readonly byte[] inverse = { 0x8, 0xf, 0x6, 0x1, 0x5, 0x2, 0xb, 0xc,
+ 0x3, 0x4, 0xd, 0xa, 0xe, 0x9, 0x0, 0x7 };
+
+ private readonly IAsymmetricBlockCipher engine;
+ private bool forEncryption;
+ private int bitSize;
+ private int padBits = 0;
+ private BigInteger modulus;
+
+ public ISO9796d1Encoding(
+ IAsymmetricBlockCipher cipher)
+ {
+ this.engine = cipher;
+ }
+
+ public string AlgorithmName
+ {
+ get { return engine.AlgorithmName + "/ISO9796-1Padding"; }
+ }
+
+ public IAsymmetricBlockCipher GetUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ RsaKeyParameters kParam;
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+ kParam = (RsaKeyParameters)rParam.Parameters;
+ }
+ else
+ {
+ kParam = (RsaKeyParameters)parameters;
+ }
+
+ engine.Init(forEncryption, parameters);
+
+ modulus = kParam.Modulus;
+ bitSize = modulus.BitLength;
+
+ this.forEncryption = forEncryption;
+ }
+
+ /**
+ * return the input block size. The largest message we can process
+ * is (key_size_in_bits + 3)/16, which in our world comes to
+ * key_size_in_bytes / 2.
+ */
+ public int GetInputBlockSize()
+ {
+ int baseBlockSize = engine.GetInputBlockSize();
+
+ if (forEncryption)
+ {
+ return (baseBlockSize + 1) / 2;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ /**
+ * return the maximum possible size for the output.
+ */
+ public int GetOutputBlockSize()
+ {
+ int baseBlockSize = engine.GetOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return (baseBlockSize + 1) / 2;
+ }
+ }
+
+ /**
+ * set the number of bits in the next message to be treated as
+ * pad bits.
+ */
+ public void SetPadBits(
+ int padBits)
+ {
+ if (padBits > 7)
+ {
+ throw new ArgumentException("padBits > 7");
+ }
+
+ this.padBits = padBits;
+ }
+
+ /**
+ * retrieve the number of pad bits in the last decoded message.
+ */
+ public int GetPadBits()
+ {
+ return padBits;
+ }
+
+ public byte[] ProcessBlock(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (forEncryption)
+ {
+ return EncodeBlock(input, inOff, length);
+ }
+ else
+ {
+ return DecodeBlock(input, inOff, length);
+ }
+ }
+
+ private byte[] EncodeBlock(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ byte[] block = new byte[(bitSize + 7) / 8];
+ int r = padBits + 1;
+ int z = inLen;
+ int t = (bitSize + 13) / 16;
+
+ for (int i = 0; i < t; i += z)
+ {
+ if (i > t - z)
+ {
+ Array.Copy(input, inOff + inLen - (t - i),
+ block, block.Length - t, t - i);
+ }
+ else
+ {
+ Array.Copy(input, inOff, block, block.Length - (i + z), z);
+ }
+ }
+
+ for (int i = block.Length - 2 * t; i != block.Length; i += 2)
+ {
+ byte val = block[block.Length - t + i / 2];
+
+ block[i] = (byte)((shadows[(uint) (val & 0xff) >> 4] << 4)
+ | shadows[val & 0x0f]);
+ block[i + 1] = val;
+ }
+
+ block[block.Length - 2 * z] ^= (byte) r;
+ block[block.Length - 1] = (byte)((block[block.Length - 1] << 4) | 0x06);
+
+ int maxBit = (8 - (bitSize - 1) % 8);
+ int offSet = 0;
+
+ if (maxBit != 8)
+ {
+ block[0] &= (byte) ((ushort) 0xff >> maxBit);
+ block[0] |= (byte) ((ushort) 0x80 >> maxBit);
+ }
+ else
+ {
+ block[0] = 0x00;
+ block[1] |= 0x80;
+ offSet = 1;
+ }
+
+ return engine.ProcessBlock(block, offSet, block.Length - offSet);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block is not a valid ISO 9796 bit string
+ */
+ private byte[] DecodeBlock(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ byte[] block = engine.ProcessBlock(input, inOff, inLen);
+ int r = 1;
+ int t = (bitSize + 13) / 16;
+
+ BigInteger iS = new BigInteger(1, block);
+ BigInteger iR;
+ if (iS.Mod(Sixteen).Equals(Six))
+ {
+ iR = iS;
+ }
+ else
+ {
+ iR = modulus.Subtract(iS);
+
+ if (!iR.Mod(Sixteen).Equals(Six))
+ throw new InvalidCipherTextException("resulting integer iS or (modulus - iS) is not congruent to 6 mod 16");
+ }
+
+ block = iR.ToByteArrayUnsigned();
+
+ if ((block[block.Length - 1] & 0x0f) != 0x6)
+ throw new InvalidCipherTextException("invalid forcing byte in block");
+
+ block[block.Length - 1] =
+ (byte)(((ushort)(block[block.Length - 1] & 0xff) >> 4)
+ | ((inverse[(block[block.Length - 2] & 0xff) >> 4]) << 4));
+
+ block[0] = (byte)((shadows[(uint) (block[1] & 0xff) >> 4] << 4)
+ | shadows[block[1] & 0x0f]);
+
+ bool boundaryFound = false;
+ int boundary = 0;
+
+ for (int i = block.Length - 1; i >= block.Length - 2 * t; i -= 2)
+ {
+ int val = ((shadows[(uint) (block[i] & 0xff) >> 4] << 4)
+ | shadows[block[i] & 0x0f]);
+
+ if (((block[i - 1] ^ val) & 0xff) != 0)
+ {
+ if (!boundaryFound)
+ {
+ boundaryFound = true;
+ r = (block[i - 1] ^ val) & 0xff;
+ boundary = i - 1;
+ }
+ else
+ {
+ throw new InvalidCipherTextException("invalid tsums in block");
+ }
+ }
+ }
+
+ block[boundary] = 0;
+
+ byte[] nblock = new byte[(block.Length - boundary) / 2];
+
+ for (int i = 0; i < nblock.Length; i++)
+ {
+ nblock[i] = block[2 * i + boundary + 1];
+ }
+
+ padBits = r - 1;
+
+ return nblock;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/encodings/OaepEncoding.cs b/Crypto/src/crypto/encodings/OaepEncoding.cs
new file mode 100644
index 000000000..81561e7f5
--- /dev/null
+++ b/Crypto/src/crypto/encodings/OaepEncoding.cs
@@ -0,0 +1,345 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Encodings
+{
+ /**
+ * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
+ */
+ public class OaepEncoding
+ : IAsymmetricBlockCipher
+ {
+ private byte[] defHash;
+ private IDigest hash;
+ private IDigest mgf1Hash;
+
+ private IAsymmetricBlockCipher engine;
+ private SecureRandom random;
+ private bool forEncryption;
+
+ public OaepEncoding(
+ IAsymmetricBlockCipher cipher)
+ : this(cipher, new Sha1Digest(), null)
+ {
+ }
+
+ public OaepEncoding(
+ IAsymmetricBlockCipher cipher,
+ IDigest hash)
+ : this(cipher, hash, null)
+ {
+ }
+
+ public OaepEncoding(
+ IAsymmetricBlockCipher cipher,
+ IDigest hash,
+ byte[] encodingParams)
+ : this(cipher, hash, hash, encodingParams)
+ {
+ }
+
+ public OaepEncoding(
+ IAsymmetricBlockCipher cipher,
+ IDigest hash,
+ IDigest mgf1Hash,
+ byte[] encodingParams)
+ {
+ this.engine = cipher;
+ this.hash = hash;
+ this.mgf1Hash = mgf1Hash;
+ this.defHash = new byte[hash.GetDigestSize()];
+
+ if (encodingParams != null)
+ {
+ hash.BlockUpdate(encodingParams, 0, encodingParams.Length);
+ }
+
+ hash.DoFinal(defHash, 0);
+ }
+
+ public IAsymmetricBlockCipher GetUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public string AlgorithmName
+ {
+ get { return engine.AlgorithmName + "/OAEPPadding"; }
+ }
+
+ public void Init(
+ bool forEncryption,
+ ICipherParameters param)
+ {
+ if (param is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+ this.random = rParam.Random;
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ engine.Init(forEncryption, param);
+
+ this.forEncryption = forEncryption;
+ }
+
+ public int GetInputBlockSize()
+ {
+ int baseBlockSize = engine.GetInputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize - 1 - 2 * defHash.Length;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ public int GetOutputBlockSize()
+ {
+ int baseBlockSize = engine.GetOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return baseBlockSize - 1 - 2 * defHash.Length;
+ }
+ }
+
+ public byte[] ProcessBlock(
+ byte[] inBytes,
+ int inOff,
+ int inLen)
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(inBytes, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(inBytes, inOff, inLen);
+ }
+ }
+
+ private byte[] encodeBlock(
+ byte[] inBytes,
+ int inOff,
+ int inLen)
+ {
+ byte[] block = new byte[GetInputBlockSize() + 1 + 2 * defHash.Length];
+
+ //
+ // copy in the message
+ //
+ Array.Copy(inBytes, inOff, block, block.Length - inLen, inLen);
+
+ //
+ // add sentinel
+ //
+ block[block.Length - inLen - 1] = 0x01;
+
+ //
+ // as the block is already zeroed - there's no need to add PS (the >= 0 pad of 0)
+ //
+
+ //
+ // add the hash of the encoding params.
+ //
+ Array.Copy(defHash, 0, block, defHash.Length, defHash.Length);
+
+ //
+ // generate the seed.
+ //
+ byte[] seed = random.GenerateSeed(defHash.Length);
+
+ //
+ // mask the message block.
+ //
+ byte[] mask = maskGeneratorFunction1(seed, 0, seed.Length, block.Length - defHash.Length);
+
+ for (int i = defHash.Length; i != block.Length; i++)
+ {
+ block[i] ^= mask[i - defHash.Length];
+ }
+
+ //
+ // add in the seed
+ //
+ Array.Copy(seed, 0, block, 0, defHash.Length);
+
+ //
+ // mask the seed.
+ //
+ mask = maskGeneratorFunction1(
+ block, defHash.Length, block.Length - defHash.Length, defHash.Length);
+
+ for (int i = 0; i != defHash.Length; i++)
+ {
+ block[i] ^= mask[i];
+ }
+
+ return engine.ProcessBlock(block, 0, block.Length);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block turns out to
+ * be badly formatted.
+ */
+ private byte[] decodeBlock(
+ byte[] inBytes,
+ int inOff,
+ int inLen)
+ {
+ byte[] data = engine.ProcessBlock(inBytes, inOff, inLen);
+ byte[] block;
+
+ //
+ // as we may have zeros in our leading bytes for the block we produced
+ // on encryption, we need to make sure our decrypted block comes back
+ // the same size.
+ //
+ if (data.Length < engine.GetOutputBlockSize())
+ {
+ block = new byte[engine.GetOutputBlockSize()];
+
+ Array.Copy(data, 0, block, block.Length - data.Length, data.Length);
+ }
+ else
+ {
+ block = data;
+ }
+
+ if (block.Length < (2 * defHash.Length) + 1)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+
+ //
+ // unmask the seed.
+ //
+ byte[] mask = maskGeneratorFunction1(
+ block, defHash.Length, block.Length - defHash.Length, defHash.Length);
+
+ for (int i = 0; i != defHash.Length; i++)
+ {
+ block[i] ^= mask[i];
+ }
+
+ //
+ // unmask the message block.
+ //
+ mask = maskGeneratorFunction1(block, 0, defHash.Length, block.Length - defHash.Length);
+
+ for (int i = defHash.Length; i != block.Length; i++)
+ {
+ block[i] ^= mask[i - defHash.Length];
+ }
+
+ //
+ // check the hash of the encoding params.
+ //
+ for (int i = 0; i != defHash.Length; i++)
+ {
+ if (defHash[i] != block[defHash.Length + i])
+ {
+ throw new InvalidCipherTextException("data hash wrong");
+ }
+ }
+
+ //
+ // find the data block
+ //
+ int start;
+ for (start = 2 * defHash.Length; start != block.Length; start++)
+ {
+ if (block[start] != 0)
+ {
+ break;
+ }
+ }
+
+ if (start >= (block.Length - 1) || block[start] != 1)
+ {
+ throw new InvalidCipherTextException("data start wrong " + start);
+ }
+
+ start++;
+
+ //
+ // extract the data block
+ //
+ byte[] output = new byte[block.Length - start];
+
+ Array.Copy(block, start, output, 0, output.Length);
+
+ return output;
+ }
+
+ /**
+ * int to octet string.
+ */
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)((uint)i >> 24);
+ sp[1] = (byte)((uint)i >> 16);
+ sp[2] = (byte)((uint)i >> 8);
+ sp[3] = (byte)((uint)i >> 0);
+ }
+
+ /**
+ * mask generator function, as described in PKCS1v2.
+ */
+ private byte[] maskGeneratorFunction1(
+ byte[] Z,
+ int zOff,
+ int zLen,
+ int length)
+ {
+ byte[] mask = new byte[length];
+ byte[] hashBuf = new byte[mgf1Hash.GetDigestSize()];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ hash.Reset();
+
+ do
+ {
+ ItoOSP(counter, C);
+
+ mgf1Hash.BlockUpdate(Z, zOff, zLen);
+ mgf1Hash.BlockUpdate(C, 0, C.Length);
+ mgf1Hash.DoFinal(hashBuf, 0);
+
+ Array.Copy(hashBuf, 0, mask, counter * hashBuf.Length, hashBuf.Length);
+ }
+ while (++counter < (length / hashBuf.Length));
+
+ if ((counter * hashBuf.Length) < length)
+ {
+ ItoOSP(counter, C);
+
+ mgf1Hash.BlockUpdate(Z, zOff, zLen);
+ mgf1Hash.BlockUpdate(C, 0, C.Length);
+ mgf1Hash.DoFinal(hashBuf, 0);
+
+ Array.Copy(hashBuf, 0, mask, counter * hashBuf.Length, mask.Length - (counter * hashBuf.Length));
+ }
+
+ return mask;
+ }
+ }
+}
+
diff --git a/Crypto/src/crypto/encodings/Pkcs1Encoding.cs b/Crypto/src/crypto/encodings/Pkcs1Encoding.cs
new file mode 100644
index 000000000..d2225a7d4
--- /dev/null
+++ b/Crypto/src/crypto/encodings/Pkcs1Encoding.cs
@@ -0,0 +1,232 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Encodings
+{
+ /**
+ * this does your basic Pkcs 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see Pkcs1 Version 2 for details.
+ */
+ public class Pkcs1Encoding
+ : IAsymmetricBlockCipher
+ {
+ /**
+ * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
+ * work with one of these set the system property Org.BouncyCastle.Pkcs1.Strict to false.
+ */
+ public const string StrictLengthEnabledProperty = "Org.BouncyCastle.Pkcs1.Strict";
+
+ private const int HeaderLength = 10;
+
+ /**
+ * The same effect can be achieved by setting the static property directly
+ * <p>
+ * The static property is checked during construction of the encoding object, it is set to
+ * true by default.
+ * </p>
+ */
+ public static bool StrictLengthEnabled
+ {
+ get { return strictLengthEnabled[0]; }
+ set { strictLengthEnabled[0] = value; }
+ }
+
+ private static readonly bool[] strictLengthEnabled;
+
+ static Pkcs1Encoding()
+ {
+ string strictProperty = Platform.GetEnvironmentVariable(StrictLengthEnabledProperty);
+
+ strictLengthEnabled = new bool[]{ strictProperty == null || strictProperty.Equals("true")};
+ }
+
+
+ private SecureRandom random;
+ private IAsymmetricBlockCipher engine;
+ private bool forEncryption;
+ private bool forPrivateKey;
+ private bool useStrictLength;
+
+ /**
+ * Basic constructor.
+ * @param cipher
+ */
+ public Pkcs1Encoding(
+ IAsymmetricBlockCipher cipher)
+ {
+ this.engine = cipher;
+ this.useStrictLength = StrictLengthEnabled;
+ }
+
+ public IAsymmetricBlockCipher GetUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public string AlgorithmName
+ {
+ get { return engine.AlgorithmName + "/PKCS1Padding"; }
+ }
+
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ AsymmetricKeyParameter kParam;
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+
+ this.random = rParam.Random;
+ kParam = (AsymmetricKeyParameter)rParam.Parameters;
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ kParam = (AsymmetricKeyParameter)parameters;
+ }
+
+ engine.Init(forEncryption, parameters);
+
+ this.forPrivateKey = kParam.IsPrivate;
+ this.forEncryption = forEncryption;
+ }
+
+ public int GetInputBlockSize()
+ {
+ int baseBlockSize = engine.GetInputBlockSize();
+
+ return forEncryption
+ ? baseBlockSize - HeaderLength
+ : baseBlockSize;
+ }
+
+ public int GetOutputBlockSize()
+ {
+ int baseBlockSize = engine.GetOutputBlockSize();
+
+ return forEncryption
+ ? baseBlockSize
+ : baseBlockSize - HeaderLength;
+ }
+
+ public byte[] ProcessBlock(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ return forEncryption
+ ? EncodeBlock(input, inOff, length)
+ : DecodeBlock(input, inOff, length);
+ }
+
+ private byte[] EncodeBlock(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ if (inLen > GetInputBlockSize())
+ throw new ArgumentException("input data too large", "inLen");
+
+ byte[] block = new byte[engine.GetInputBlockSize()];
+
+ if (forPrivateKey)
+ {
+ block[0] = 0x01; // type code 1
+
+ for (int i = 1; i != block.Length - inLen - 1; i++)
+ {
+ block[i] = (byte)0xFF;
+ }
+ }
+ else
+ {
+ random.NextBytes(block); // random fill
+
+ block[0] = 0x02; // type code 2
+
+ //
+ // a zero byte marks the end of the padding, so all
+ // the pad bytes must be non-zero.
+ //
+ for (int i = 1; i != block.Length - inLen - 1; i++)
+ {
+ while (block[i] == 0)
+ {
+ block[i] = (byte)random.NextInt();
+ }
+ }
+ }
+
+ block[block.Length - inLen - 1] = 0x00; // mark the end of the padding
+ Array.Copy(input, inOff, block, block.Length - inLen, inLen);
+
+ return engine.ProcessBlock(block, 0, block.Length);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block is not in Pkcs1 format.
+ */
+ private byte[] DecodeBlock(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ byte[] block = engine.ProcessBlock(input, inOff, inLen);
+
+ if (block.Length < GetOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block truncated");
+ }
+
+ byte type = block[0];
+
+ if (type != 1 && type != 2)
+ {
+ throw new InvalidCipherTextException("unknown block type");
+ }
+
+ if (useStrictLength && block.Length != engine.GetOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block incorrect size");
+ }
+
+ //
+ // find and extract the message block.
+ //
+ int start;
+ for (start = 1; start != block.Length; start++)
+ {
+ byte pad = block[start];
+
+ if (pad == 0)
+ {
+ break;
+ }
+
+ if (type == 1 && pad != (byte)0xff)
+ {
+ throw new InvalidCipherTextException("block padding incorrect");
+ }
+ }
+
+ start++; // data should start at the next byte
+
+ if (start > block.Length || start < HeaderLength)
+ {
+ throw new InvalidCipherTextException("no data in block");
+ }
+
+ byte[] result = new byte[block.Length - start];
+
+ Array.Copy(block, start, result, 0, result.Length);
+
+ return result;
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/AesEngine.cs b/Crypto/src/crypto/engines/AesEngine.cs
new file mode 100644
index 000000000..4211a9559
--- /dev/null
+++ b/Crypto/src/crypto/engines/AesEngine.cs
@@ -0,0 +1,525 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * an implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ *
+ * There are three levels of tradeoff of speed vs memory
+ * Because java has no preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
+ * and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
+ * adding 12 rotate operations per round to compute the values contained in the other tables from
+ * the contents of the first.
+ *
+ * The slowest version uses no static tables at all and computes the values in each round.
+ * </p>
+ * <p>
+ * This file contains the middle performance version with 2Kbytes of static tables for round precomputation.
+ * </p>
+ */
+ public class AesEngine
+ : IBlockCipher
+ {
+ // The S box
+ private static readonly byte[] S =
+ {
+ 99, 124, 119, 123, 242, 107, 111, 197,
+ 48, 1, 103, 43, 254, 215, 171, 118,
+ 202, 130, 201, 125, 250, 89, 71, 240,
+ 173, 212, 162, 175, 156, 164, 114, 192,
+ 183, 253, 147, 38, 54, 63, 247, 204,
+ 52, 165, 229, 241, 113, 216, 49, 21,
+ 4, 199, 35, 195, 24, 150, 5, 154,
+ 7, 18, 128, 226, 235, 39, 178, 117,
+ 9, 131, 44, 26, 27, 110, 90, 160,
+ 82, 59, 214, 179, 41, 227, 47, 132,
+ 83, 209, 0, 237, 32, 252, 177, 91,
+ 106, 203, 190, 57, 74, 76, 88, 207,
+ 208, 239, 170, 251, 67, 77, 51, 133,
+ 69, 249, 2, 127, 80, 60, 159, 168,
+ 81, 163, 64, 143, 146, 157, 56, 245,
+ 188, 182, 218, 33, 16, 255, 243, 210,
+ 205, 12, 19, 236, 95, 151, 68, 23,
+ 196, 167, 126, 61, 100, 93, 25, 115,
+ 96, 129, 79, 220, 34, 42, 144, 136,
+ 70, 238, 184, 20, 222, 94, 11, 219,
+ 224, 50, 58, 10, 73, 6, 36, 92,
+ 194, 211, 172, 98, 145, 149, 228, 121,
+ 231, 200, 55, 109, 141, 213, 78, 169,
+ 108, 86, 244, 234, 101, 122, 174, 8,
+ 186, 120, 37, 46, 28, 166, 180, 198,
+ 232, 221, 116, 31, 75, 189, 139, 138,
+ 112, 62, 181, 102, 72, 3, 246, 14,
+ 97, 53, 87, 185, 134, 193, 29, 158,
+ 225, 248, 152, 17, 105, 217, 142, 148,
+ 155, 30, 135, 233, 206, 85, 40, 223,
+ 140, 161, 137, 13, 191, 230, 66, 104,
+ 65, 153, 45, 15, 176, 84, 187, 22,
+ };
+
+ // The inverse S-box
+ private static readonly byte[] Si =
+ {
+ 82, 9, 106, 213, 48, 54, 165, 56,
+ 191, 64, 163, 158, 129, 243, 215, 251,
+ 124, 227, 57, 130, 155, 47, 255, 135,
+ 52, 142, 67, 68, 196, 222, 233, 203,
+ 84, 123, 148, 50, 166, 194, 35, 61,
+ 238, 76, 149, 11, 66, 250, 195, 78,
+ 8, 46, 161, 102, 40, 217, 36, 178,
+ 118, 91, 162, 73, 109, 139, 209, 37,
+ 114, 248, 246, 100, 134, 104, 152, 22,
+ 212, 164, 92, 204, 93, 101, 182, 146,
+ 108, 112, 72, 80, 253, 237, 185, 218,
+ 94, 21, 70, 87, 167, 141, 157, 132,
+ 144, 216, 171, 0, 140, 188, 211, 10,
+ 247, 228, 88, 5, 184, 179, 69, 6,
+ 208, 44, 30, 143, 202, 63, 15, 2,
+ 193, 175, 189, 3, 1, 19, 138, 107,
+ 58, 145, 17, 65, 79, 103, 220, 234,
+ 151, 242, 207, 206, 240, 180, 230, 115,
+ 150, 172, 116, 34, 231, 173, 53, 133,
+ 226, 249, 55, 232, 28, 117, 223, 110,
+ 71, 241, 26, 113, 29, 41, 197, 137,
+ 111, 183, 98, 14, 170, 24, 190, 27,
+ 252, 86, 62, 75, 198, 210, 121, 32,
+ 154, 219, 192, 254, 120, 205, 90, 244,
+ 31, 221, 168, 51, 136, 7, 199, 49,
+ 177, 18, 16, 89, 39, 128, 236, 95,
+ 96, 81, 127, 169, 25, 181, 74, 13,
+ 45, 229, 122, 159, 147, 201, 156, 239,
+ 160, 224, 59, 77, 174, 42, 245, 176,
+ 200, 235, 187, 60, 131, 83, 153, 97,
+ 23, 43, 4, 126, 186, 119, 214, 38,
+ 225, 105, 20, 99, 85, 33, 12, 125,
+ };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static readonly byte[] rcon =
+ {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91
+ };
+
+ // precomputation tables of calculations for rounds
+ private static readonly uint[] T0 =
+ {
+ 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
+ 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
+ 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
+ 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
+ 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
+ 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
+ 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
+ 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
+ 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
+ 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
+ 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
+ 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
+ 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
+ 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
+ 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
+ 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
+ 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
+ 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
+ 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
+ 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
+ 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
+ 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
+ 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
+ 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
+ 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
+ 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
+ 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
+ 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
+ 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+ 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
+ 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
+ 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
+ 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
+ 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
+ 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
+ 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
+ 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
+ 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
+ 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
+ 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
+ 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
+ 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
+ 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
+ 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
+ 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
+ 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
+ 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
+ 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
+ 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
+ 0x3a16162c
+ };
+
+ private static readonly uint[] Tinv0 =
+ {
+ 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
+ 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
+ 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
+ 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
+ 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
+ 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
+ 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
+ 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
+ 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
+ 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
+ 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
+ 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+ 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
+ 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
+ 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
+ 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
+ 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
+ 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
+ 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
+ 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
+ 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
+ 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
+ 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
+ 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
+ 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
+ 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
+ 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
+ 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
+ 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
+ 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
+ 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
+ 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
+ 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
+ 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
+ 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
+ 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
+ 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
+ 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
+ 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
+ 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
+ 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
+ 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
+ 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
+ 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
+ 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
+ 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
+ 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
+ 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+ 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
+ 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
+ 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
+ 0x4257b8d0
+ };
+
+ private uint Shift(
+ uint r,
+ int shift)
+ {
+ return (r >> shift) | (r << (32 - shift));
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private const uint m1 = 0x80808080;
+ private const uint m2 = 0x7f7f7f7f;
+ private const uint m3 = 0x0000001b;
+
+ private uint FFmulX(
+ uint x)
+ {
+ return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3);
+ }
+
+ /*
+ The following defines provide alternative definitions of FFmulX that might
+ give improved performance if a fast 32-bit multiply is not available.
+
+ private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+ private static final int m4 = 0x1b1b1b1b;
+ private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+ */
+
+ private uint Inv_Mcol(
+ uint x)
+ {
+ uint f2 = FFmulX(x);
+ uint f4 = FFmulX(f2);
+ uint f8 = FFmulX(f4);
+ uint f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24);
+ }
+
+ private uint SubWord(
+ uint x)
+ {
+ return (uint)S[x&255]
+ | (((uint)S[(x>>8)&255]) << 8)
+ | (((uint)S[(x>>16)&255]) << 16)
+ | (((uint)S[(x>>24)&255]) << 24);
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on key size and block size
+ * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
+ * This code is written assuming those are the only possible values
+ */
+ private uint[,] GenerateWorkingKey(
+ byte[] key,
+ bool forEncryption)
+ {
+ int KC = key.Length / 4; // key length in words
+ int t;
+
+ if ((KC != 4) && (KC != 6) && (KC != 8))
+ throw new ArgumentException("Key length not 128/192/256 bits.");
+
+ ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
+ uint[,] W = new uint[ROUNDS+1, 4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ for (int i = 0; i < key.Length; t++)
+ {
+ W[t >> 2, t & 3] = Pack.LE_To_UInt32(key, i);
+ i+=4;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (int i = KC; (i < k); i++)
+ {
+ uint temp = W[(i-1)>>2, (i-1)&3];
+ if ((i % KC) == 0)
+ {
+ temp = SubWord(Shift(temp, 8)) ^ rcon[(i / KC)-1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = SubWord(temp);
+ }
+
+ W[i>>2, i&3] = W[(i - KC)>>2, (i-KC)&3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[j, i] = Inv_Mcol(W[j, i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private uint[,] WorkingKey;
+ private uint C0, C1, C2, C3;
+ private bool forEncryption;
+
+ private const int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AesEngine()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ KeyParameter keyParameter = parameters as KeyParameter;
+
+ if (keyParameter == null)
+ throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().Name);
+
+ WorkingKey = GenerateWorkingKey(keyParameter.GetKey(), forEncryption);
+
+ this.forEncryption = forEncryption;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "AES"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new InvalidOperationException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ UnPackBlock(input, inOff);
+
+ if (forEncryption)
+ {
+ EncryptBlock(WorkingKey);
+ }
+ else
+ {
+ DecryptBlock(WorkingKey);
+ }
+
+ PackBlock(output, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ }
+
+ private void UnPackBlock(
+ byte[] bytes,
+ int off)
+ {
+ C0 = Pack.LE_To_UInt32(bytes, off);
+ C1 = Pack.LE_To_UInt32(bytes, off + 4);
+ C2 = Pack.LE_To_UInt32(bytes, off + 8);
+ C3 = Pack.LE_To_UInt32(bytes, off + 12);
+ }
+
+ private void PackBlock(
+ byte[] bytes,
+ int off)
+ {
+ Pack.UInt32_To_LE(C0, bytes, off);
+ Pack.UInt32_To_LE(C1, bytes, off + 4);
+ Pack.UInt32_To_LE(C2, bytes, off + 8);
+ Pack.UInt32_To_LE(C3, bytes, off + 12);
+ }
+
+ private void EncryptBlock(
+ uint[,] KW)
+ {
+ uint r, r0, r1, r2, r3;
+
+ C0 ^= KW[0, 0];
+ C1 ^= KW[0, 1];
+ C2 ^= KW[0, 2];
+ C3 ^= KW[0, 3];
+
+ for (r = 1; r < ROUNDS - 1;)
+ {
+ r0 = T0[C0&255] ^ Shift(T0[(C1>>8)&255], 24) ^ Shift(T0[(C2>>16)&255],16) ^ Shift(T0[(C3>>24)&255],8) ^ KW[r,0];
+ r1 = T0[C1&255] ^ Shift(T0[(C2>>8)&255], 24) ^ Shift(T0[(C3>>16)&255], 16) ^ Shift(T0[(C0>>24)&255], 8) ^ KW[r,1];
+ r2 = T0[C2&255] ^ Shift(T0[(C3>>8)&255], 24) ^ Shift(T0[(C0>>16)&255], 16) ^ Shift(T0[(C1>>24)&255], 8) ^ KW[r,2];
+ r3 = T0[C3&255] ^ Shift(T0[(C0>>8)&255], 24) ^ Shift(T0[(C1>>16)&255], 16) ^ Shift(T0[(C2>>24)&255], 8) ^ KW[r++,3];
+ C0 = T0[r0&255] ^ Shift(T0[(r1>>8)&255], 24) ^ Shift(T0[(r2>>16)&255], 16) ^ Shift(T0[(r3>>24)&255], 8) ^ KW[r,0];
+ C1 = T0[r1&255] ^ Shift(T0[(r2>>8)&255], 24) ^ Shift(T0[(r3>>16)&255], 16) ^ Shift(T0[(r0>>24)&255], 8) ^ KW[r,1];
+ C2 = T0[r2&255] ^ Shift(T0[(r3>>8)&255], 24) ^ Shift(T0[(r0>>16)&255], 16) ^ Shift(T0[(r1>>24)&255], 8) ^ KW[r,2];
+ C3 = T0[r3&255] ^ Shift(T0[(r0>>8)&255], 24) ^ Shift(T0[(r1>>16)&255], 16) ^ Shift(T0[(r2>>24)&255], 8) ^ KW[r++,3];
+ }
+
+ r0 = T0[C0&255] ^ Shift(T0[(C1>>8)&255], 24) ^ Shift(T0[(C2>>16)&255], 16) ^ Shift(T0[(C3>>24)&255], 8) ^ KW[r,0];
+ r1 = T0[C1&255] ^ Shift(T0[(C2>>8)&255], 24) ^ Shift(T0[(C3>>16)&255], 16) ^ Shift(T0[(C0>>24)&255], 8) ^ KW[r,1];
+ r2 = T0[C2&255] ^ Shift(T0[(C3>>8)&255], 24) ^ Shift(T0[(C0>>16)&255], 16) ^ Shift(T0[(C1>>24)&255], 8) ^ KW[r,2];
+ r3 = T0[C3&255] ^ Shift(T0[(C0>>8)&255], 24) ^ Shift(T0[(C1>>16)&255], 16) ^ Shift(T0[(C2>>24)&255], 8) ^ KW[r++,3];
+
+ // the final round's table is a simple function of S so we don't use a whole other four tables for it
+
+ C0 = (uint)S[r0&255] ^ (((uint)S[(r1>>8)&255])<<8) ^ (((uint)S[(r2>>16)&255])<<16) ^ (((uint)S[(r3>>24)&255])<<24) ^ KW[r,0];
+ C1 = (uint)S[r1&255] ^ (((uint)S[(r2>>8)&255])<<8) ^ (((uint)S[(r3>>16)&255])<<16) ^ (((uint)S[(r0>>24)&255])<<24) ^ KW[r,1];
+ C2 = (uint)S[r2&255] ^ (((uint)S[(r3>>8)&255])<<8) ^ (((uint)S[(r0>>16)&255])<<16) ^ (((uint)S[(r1>>24)&255])<<24) ^ KW[r,2];
+ C3 = (uint)S[r3&255] ^ (((uint)S[(r0>>8)&255])<<8) ^ (((uint)S[(r1>>16)&255])<<16) ^ (((uint)S[(r2>>24)&255])<<24) ^ KW[r,3];
+ }
+
+ private void DecryptBlock(
+ uint[,] KW)
+ {
+ int r;
+ uint r0, r1, r2, r3;
+
+ C0 ^= KW[ROUNDS,0];
+ C1 ^= KW[ROUNDS,1];
+ C2 ^= KW[ROUNDS,2];
+ C3 ^= KW[ROUNDS,3];
+
+ for (r = ROUNDS-1; r>1;)
+ {
+ r0 = Tinv0[C0&255] ^ Shift(Tinv0[(C3>>8)&255], 24) ^ Shift(Tinv0[(C2>>16)&255], 16) ^ Shift(Tinv0[(C1>>24)&255], 8) ^ KW[r,0];
+ r1 = Tinv0[C1&255] ^ Shift(Tinv0[(C0>>8)&255], 24) ^ Shift(Tinv0[(C3>>16)&255], 16) ^ Shift(Tinv0[(C2>>24)&255], 8) ^ KW[r,1];
+ r2 = Tinv0[C2&255] ^ Shift(Tinv0[(C1>>8)&255], 24) ^ Shift(Tinv0[(C0>>16)&255], 16) ^ Shift(Tinv0[(C3>>24)&255], 8) ^ KW[r,2];
+ r3 = Tinv0[C3&255] ^ Shift(Tinv0[(C2>>8)&255], 24) ^ Shift(Tinv0[(C1>>16)&255], 16) ^ Shift(Tinv0[(C0>>24)&255], 8) ^ KW[r--,3];
+ C0 = Tinv0[r0&255] ^ Shift(Tinv0[(r3>>8)&255], 24) ^ Shift(Tinv0[(r2>>16)&255], 16) ^ Shift(Tinv0[(r1>>24)&255], 8) ^ KW[r,0];
+ C1 = Tinv0[r1&255] ^ Shift(Tinv0[(r0>>8)&255], 24) ^ Shift(Tinv0[(r3>>16)&255], 16) ^ Shift(Tinv0[(r2>>24)&255], 8) ^ KW[r,1];
+ C2 = Tinv0[r2&255] ^ Shift(Tinv0[(r1>>8)&255], 24) ^ Shift(Tinv0[(r0>>16)&255], 16) ^ Shift(Tinv0[(r3>>24)&255], 8) ^ KW[r,2];
+ C3 = Tinv0[r3&255] ^ Shift(Tinv0[(r2>>8)&255], 24) ^ Shift(Tinv0[(r1>>16)&255], 16) ^ Shift(Tinv0[(r0>>24)&255], 8) ^ KW[r--,3];
+ }
+
+ r0 = Tinv0[C0&255] ^ Shift(Tinv0[(C3>>8)&255], 24) ^ Shift(Tinv0[(C2>>16)&255], 16) ^ Shift(Tinv0[(C1>>24)&255], 8) ^ KW[r,0];
+ r1 = Tinv0[C1&255] ^ Shift(Tinv0[(C0>>8)&255], 24) ^ Shift(Tinv0[(C3>>16)&255], 16) ^ Shift(Tinv0[(C2>>24)&255], 8) ^ KW[r,1];
+ r2 = Tinv0[C2&255] ^ Shift(Tinv0[(C1>>8)&255], 24) ^ Shift(Tinv0[(C0>>16)&255], 16) ^ Shift(Tinv0[(C3>>24)&255], 8) ^ KW[r,2];
+ r3 = Tinv0[C3&255] ^ Shift(Tinv0[(C2>>8)&255], 24) ^ Shift(Tinv0[(C1>>16)&255], 16) ^ Shift(Tinv0[(C0>>24)&255], 8) ^ KW[r,3];
+
+ // the final round's table is a simple function of Si so we don't use a whole other four tables for it
+
+ C0 = (uint)Si[r0&255] ^ (((uint)Si[(r3>>8)&255])<<8) ^ (((uint)Si[(r2>>16)&255])<<16) ^ (((uint)Si[(r1>>24)&255])<<24) ^ KW[0,0];
+ C1 = (uint)Si[r1&255] ^ (((uint)Si[(r0>>8)&255])<<8) ^ (((uint)Si[(r3>>16)&255])<<16) ^ (((uint)Si[(r2>>24)&255])<<24) ^ KW[0,1];
+ C2 = (uint)Si[r2&255] ^ (((uint)Si[(r1>>8)&255])<<8) ^ (((uint)Si[(r0>>16)&255])<<16) ^ (((uint)Si[(r3>>24)&255])<<24) ^ KW[0,2];
+ C3 = (uint)Si[r3&255] ^ (((uint)Si[(r2>>8)&255])<<8) ^ (((uint)Si[(r1>>16)&255])<<16) ^ (((uint)Si[(r0>>24)&255])<<24) ^ KW[0,3];
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/AesFastEngine.cs b/Crypto/src/crypto/engines/AesFastEngine.cs
new file mode 100644
index 000000000..603b5ce4d
--- /dev/null
+++ b/Crypto/src/crypto/engines/AesFastEngine.cs
@@ -0,0 +1,853 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * an implementation of the AES (Rijndael)), from FIPS-197.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ *
+ * There are three levels of tradeoff of speed vs memory
+ * Because java has no preprocessor), they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations), 4 256 word tables for encryption
+ * and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each), for a total of 2Kbytes),
+ * adding 12 rotate operations per round to compute the values contained in the other tables from
+ * the contents of the first
+ *
+ * The slowest version uses no static tables at all and computes the values in each round
+ * </p>
+ * <p>
+ * This file contains the fast version with 8Kbytes of static tables for round precomputation
+ * </p>
+ */
+ public class AesFastEngine
+ : IBlockCipher
+ {
+ // The S box
+ private static readonly byte[] S =
+ {
+ 99, 124, 119, 123, 242, 107, 111, 197,
+ 48, 1, 103, 43, 254, 215, 171, 118,
+ 202, 130, 201, 125, 250, 89, 71, 240,
+ 173, 212, 162, 175, 156, 164, 114, 192,
+ 183, 253, 147, 38, 54, 63, 247, 204,
+ 52, 165, 229, 241, 113, 216, 49, 21,
+ 4, 199, 35, 195, 24, 150, 5, 154,
+ 7, 18, 128, 226, 235, 39, 178, 117,
+ 9, 131, 44, 26, 27, 110, 90, 160,
+ 82, 59, 214, 179, 41, 227, 47, 132,
+ 83, 209, 0, 237, 32, 252, 177, 91,
+ 106, 203, 190, 57, 74, 76, 88, 207,
+ 208, 239, 170, 251, 67, 77, 51, 133,
+ 69, 249, 2, 127, 80, 60, 159, 168,
+ 81, 163, 64, 143, 146, 157, 56, 245,
+ 188, 182, 218, 33, 16, 255, 243, 210,
+ 205, 12, 19, 236, 95, 151, 68, 23,
+ 196, 167, 126, 61, 100, 93, 25, 115,
+ 96, 129, 79, 220, 34, 42, 144, 136,
+ 70, 238, 184, 20, 222, 94, 11, 219,
+ 224, 50, 58, 10, 73, 6, 36, 92,
+ 194, 211, 172, 98, 145, 149, 228, 121,
+ 231, 200, 55, 109, 141, 213, 78, 169,
+ 108, 86, 244, 234, 101, 122, 174, 8,
+ 186, 120, 37, 46, 28, 166, 180, 198,
+ 232, 221, 116, 31, 75, 189, 139, 138,
+ 112, 62, 181, 102, 72, 3, 246, 14,
+ 97, 53, 87, 185, 134, 193, 29, 158,
+ 225, 248, 152, 17, 105, 217, 142, 148,
+ 155, 30, 135, 233, 206, 85, 40, 223,
+ 140, 161, 137, 13, 191, 230, 66, 104,
+ 65, 153, 45, 15, 176, 84, 187, 22,
+ };
+
+ // The inverse S-box
+ private static readonly byte[] Si =
+ {
+ 82, 9, 106, 213, 48, 54, 165, 56,
+ 191, 64, 163, 158, 129, 243, 215, 251,
+ 124, 227, 57, 130, 155, 47, 255, 135,
+ 52, 142, 67, 68, 196, 222, 233, 203,
+ 84, 123, 148, 50, 166, 194, 35, 61,
+ 238, 76, 149, 11, 66, 250, 195, 78,
+ 8, 46, 161, 102, 40, 217, 36, 178,
+ 118, 91, 162, 73, 109, 139, 209, 37,
+ 114, 248, 246, 100, 134, 104, 152, 22,
+ 212, 164, 92, 204, 93, 101, 182, 146,
+ 108, 112, 72, 80, 253, 237, 185, 218,
+ 94, 21, 70, 87, 167, 141, 157, 132,
+ 144, 216, 171, 0, 140, 188, 211, 10,
+ 247, 228, 88, 5, 184, 179, 69, 6,
+ 208, 44, 30, 143, 202, 63, 15, 2,
+ 193, 175, 189, 3, 1, 19, 138, 107,
+ 58, 145, 17, 65, 79, 103, 220, 234,
+ 151, 242, 207, 206, 240, 180, 230, 115,
+ 150, 172, 116, 34, 231, 173, 53, 133,
+ 226, 249, 55, 232, 28, 117, 223, 110,
+ 71, 241, 26, 113, 29, 41, 197, 137,
+ 111, 183, 98, 14, 170, 24, 190, 27,
+ 252, 86, 62, 75, 198, 210, 121, 32,
+ 154, 219, 192, 254, 120, 205, 90, 244,
+ 31, 221, 168, 51, 136, 7, 199, 49,
+ 177, 18, 16, 89, 39, 128, 236, 95,
+ 96, 81, 127, 169, 25, 181, 74, 13,
+ 45, 229, 122, 159, 147, 201, 156, 239,
+ 160, 224, 59, 77, 174, 42, 245, 176,
+ 200, 235, 187, 60, 131, 83, 153, 97,
+ 23, 43, 4, 126, 186, 119, 214, 38,
+ 225, 105, 20, 99, 85, 33, 12, 125,
+ };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static readonly byte[] rcon =
+ {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91
+ };
+
+ // precomputation tables of calculations for rounds
+ private static readonly uint[] T0 =
+ {
+ 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
+ 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
+ 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
+ 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
+ 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
+ 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
+ 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
+ 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
+ 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
+ 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
+ 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
+ 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
+ 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
+ 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
+ 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
+ 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
+ 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
+ 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
+ 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
+ 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
+ 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
+ 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
+ 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
+ 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
+ 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
+ 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
+ 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
+ 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
+ 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+ 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
+ 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
+ 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
+ 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
+ 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
+ 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
+ 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
+ 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
+ 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
+ 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
+ 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
+ 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
+ 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
+ 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
+ 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
+ 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
+ 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
+ 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
+ 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
+ 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
+ 0x3a16162c
+ };
+
+ private static readonly uint[] T1 =
+ {
+ 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d,
+ 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203,
+ 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
+ 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87,
+ 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec,
+ 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7,
+ 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae,
+ 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f,
+ 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293,
+ 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552,
+ 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f,
+ 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
+ 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b,
+ 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2,
+ 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761,
+ 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397,
+ 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060,
+ 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46,
+ 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8,
+ 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16,
+ 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
+ 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844,
+ 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0,
+ 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
+ 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030,
+ 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814,
+ 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc,
+ 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47,
+ 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0,
+ 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
+ 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3,
+ 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76,
+ 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db,
+ 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e,
+ 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337,
+ 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
+ 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4,
+ 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e,
+ 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
+ 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751,
+ 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd,
+ 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42,
+ 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701,
+ 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0,
+ 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938,
+ 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970,
+ 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592,
+ 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
+ 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da,
+ 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0,
+ 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6,
+ 0x16162c3a
+ };
+
+ private static readonly uint[] T2 =
+ {
+ 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2,
+ 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301,
+ 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
+ 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d,
+ 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad,
+ 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4,
+ 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93,
+ 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc,
+ 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371,
+ 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7,
+ 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05,
+ 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
+ 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09,
+ 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e,
+ 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6,
+ 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784,
+ 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020,
+ 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb,
+ 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858,
+ 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb,
+ 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
+ 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c,
+ 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040,
+ 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
+ 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010,
+ 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c,
+ 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44,
+ 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d,
+ 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060,
+ 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
+ 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8,
+ 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db,
+ 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49,
+ 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3,
+ 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4,
+ 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
+ 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c,
+ 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a,
+ 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
+ 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6,
+ 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b,
+ 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e,
+ 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6,
+ 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9,
+ 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1,
+ 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9,
+ 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287,
+ 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
+ 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf,
+ 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099,
+ 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb,
+ 0x162c3a16
+ };
+
+ private static readonly uint[] T3 =
+ {
+ 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2,
+ 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101,
+ 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
+ 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d,
+ 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad,
+ 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4,
+ 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393,
+ 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc,
+ 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171,
+ 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7,
+ 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505,
+ 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
+ 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909,
+ 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e,
+ 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6,
+ 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484,
+ 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020,
+ 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb,
+ 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858,
+ 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb,
+ 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
+ 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c,
+ 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040,
+ 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
+ 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010,
+ 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c,
+ 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444,
+ 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d,
+ 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060,
+ 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
+ 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8,
+ 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb,
+ 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949,
+ 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3,
+ 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4,
+ 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
+ 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c,
+ 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a,
+ 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
+ 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6,
+ 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b,
+ 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e,
+ 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6,
+ 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9,
+ 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1,
+ 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9,
+ 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787,
+ 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
+ 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf,
+ 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999,
+ 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb,
+ 0x2c3a1616
+ };
+
+ private static readonly uint[] Tinv0 =
+ {
+ 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
+ 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
+ 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
+ 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
+ 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
+ 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
+ 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
+ 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
+ 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
+ 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
+ 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
+ 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+ 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
+ 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
+ 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
+ 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
+ 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
+ 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
+ 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
+ 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
+ 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
+ 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
+ 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
+ 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
+ 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
+ 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
+ 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
+ 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
+ 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
+ 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
+ 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
+ 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
+ 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
+ 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
+ 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
+ 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
+ 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
+ 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
+ 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
+ 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
+ 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
+ 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
+ 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
+ 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
+ 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
+ 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
+ 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
+ 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+ 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
+ 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
+ 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
+ 0x4257b8d0
+ };
+
+ private static readonly uint[] Tinv1 =
+ {
+ 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb,
+ 0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6,
+ 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
+ 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1,
+ 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7,
+ 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3,
+ 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b,
+ 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4,
+ 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0,
+ 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19,
+ 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357,
+ 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
+ 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b,
+ 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5,
+ 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532,
+ 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51,
+ 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5,
+ 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697,
+ 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738,
+ 0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000,
+ 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
+ 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821,
+ 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2,
+ 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16,
+ 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b,
+ 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c,
+ 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5,
+ 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863,
+ 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d,
+ 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
+ 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa,
+ 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef,
+ 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf,
+ 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d,
+ 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e,
+ 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3,
+ 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09,
+ 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e,
+ 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
+ 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0,
+ 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a,
+ 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d,
+ 0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8,
+ 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e,
+ 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c,
+ 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735,
+ 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879,
+ 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
+ 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672,
+ 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de,
+ 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874,
+ 0x57b8d042
+ };
+
+ private static readonly uint[] Tinv2 =
+ {
+ 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b,
+ 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d,
+ 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
+ 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0,
+ 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f,
+ 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321,
+ 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e,
+ 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a,
+ 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077,
+ 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd,
+ 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f,
+ 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
+ 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c,
+ 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be,
+ 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1,
+ 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110,
+ 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d,
+ 0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9,
+ 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b,
+ 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000,
+ 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
+ 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6,
+ 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296,
+ 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a,
+ 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d,
+ 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07,
+ 0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b,
+ 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1,
+ 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24,
+ 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
+ 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48,
+ 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90,
+ 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81,
+ 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92,
+ 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7,
+ 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312,
+ 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978,
+ 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6,
+ 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
+ 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066,
+ 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98,
+ 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0,
+ 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f,
+ 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41,
+ 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61,
+ 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9,
+ 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce,
+ 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
+ 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3,
+ 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3,
+ 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c,
+ 0xb8d04257
+ };
+
+ private static readonly uint[] Tinv3 =
+ {
+ 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab,
+ 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76,
+ 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
+ 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe,
+ 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f,
+ 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174,
+ 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58,
+ 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace,
+ 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764,
+ 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45,
+ 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f,
+ 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
+ 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf,
+ 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05,
+ 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a,
+ 0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e,
+ 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54,
+ 0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd,
+ 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19,
+ 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000,
+ 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
+ 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c,
+ 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee,
+ 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12,
+ 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09,
+ 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775,
+ 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66,
+ 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4,
+ 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a,
+ 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
+ 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894,
+ 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033,
+ 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5,
+ 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278,
+ 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739,
+ 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225,
+ 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826,
+ 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff,
+ 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
+ 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2,
+ 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804,
+ 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef,
+ 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c,
+ 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b,
+ 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7,
+ 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961,
+ 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14,
+ 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
+ 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d,
+ 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c,
+ 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c,
+ 0xd04257b8
+ };
+
+ private uint Shift(
+ uint r,
+ int shift)
+ {
+ return (r >> shift) | (r << (32 - shift));
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private const uint m1 = 0x80808080;
+ private const uint m2 = 0x7f7f7f7f;
+ private const uint m3 = 0x0000001b;
+
+ private uint FFmulX(
+ uint x)
+ {
+ return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3);
+ }
+
+ /*
+ The following defines provide alternative definitions of FFmulX that might
+ give improved performance if a fast 32-bit multiply is not available.
+
+ private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+ private static final int m4 = 0x1b1b1b1b;
+ private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+ */
+
+ private uint Inv_Mcol(
+ uint x) {
+ uint f2 = FFmulX(x);
+ uint f4 = FFmulX(f2);
+ uint f8 = FFmulX(f4);
+ uint f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24);
+ }
+
+ private uint SubWord(
+ uint x)
+ {
+ return (uint)S[x&255]
+ | (((uint)S[(x>>8)&255]) << 8)
+ | (((uint)S[(x>>16)&255]) << 16)
+ | (((uint)S[(x>>24)&255]) << 24);
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on key size and block size
+ * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
+ * This code is written assuming those are the only possible values
+ */
+ private uint[,] GenerateWorkingKey(
+ byte[] key,
+ bool forEncryption)
+ {
+ int KC = key.Length / 4; // key length in words
+
+ if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.Length))
+ throw new ArgumentException("Key length not 128/192/256 bits.");
+
+ ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
+ uint[,] W = new uint[ROUNDS+1,4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ int t = 0;
+ for (int i = 0; i < key.Length; t++)
+ {
+ W[t >> 2,t & 3] = Pack.LE_To_UInt32(key, i);
+ i+=4;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (int i = KC; (i < k); i++)
+ {
+ uint temp = W[(i-1)>>2,(i-1)&3];
+ if ((i % KC) == 0) {
+ temp = SubWord(Shift(temp, 8)) ^ rcon[(i / KC)-1];
+ } else if ((KC > 6) && ((i % KC) == 4)) {
+ temp = SubWord(temp);
+ }
+
+ W[i>>2,i&3] = W[(i - KC)>>2,(i-KC)&3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[j,i] = Inv_Mcol(W[j,i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private uint[,] WorkingKey;
+ private uint C0, C1, C2, C3;
+ private bool forEncryption;
+
+ private const int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AesFastEngine()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().ToString());
+
+ WorkingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey(), forEncryption);
+ this.forEncryption = forEncryption;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "AES"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new InvalidOperationException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ UnPackBlock(input, inOff);
+
+ if (forEncryption)
+ {
+ EncryptBlock(WorkingKey);
+ }
+ else
+ {
+ DecryptBlock(WorkingKey);
+ }
+
+ PackBlock(output, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ }
+
+ private void UnPackBlock(
+ byte[] bytes,
+ int off)
+ {
+ C0 = Pack.LE_To_UInt32(bytes, off);
+ C1 = Pack.LE_To_UInt32(bytes, off + 4);
+ C2 = Pack.LE_To_UInt32(bytes, off + 8);
+ C3 = Pack.LE_To_UInt32(bytes, off + 12);
+ }
+
+ private void PackBlock(
+ byte[] bytes,
+ int off)
+ {
+ Pack.UInt32_To_LE(C0, bytes, off);
+ Pack.UInt32_To_LE(C1, bytes, off + 4);
+ Pack.UInt32_To_LE(C2, bytes, off + 8);
+ Pack.UInt32_To_LE(C3, bytes, off + 12);
+ }
+
+ private void EncryptBlock(
+ uint[,] KW)
+ {
+ int r;
+ uint r0, r1, r2, r3;
+
+ C0 ^= KW[0,0];
+ C1 ^= KW[0,1];
+ C2 ^= KW[0,2];
+ C3 ^= KW[0,3];
+
+ for (r = 1; r < ROUNDS - 1;)
+ {
+ r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[C3>>24] ^ KW[r,0];
+ r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[C0>>24] ^ KW[r,1];
+ r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[C1>>24] ^ KW[r,2];
+ r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[C2>>24] ^ KW[r++,3];
+ C0 = T0[r0&255] ^ T1[(r1>>8)&255] ^ T2[(r2>>16)&255] ^ T3[r3>>24] ^ KW[r,0];
+ C1 = T0[r1&255] ^ T1[(r2>>8)&255] ^ T2[(r3>>16)&255] ^ T3[r0>>24] ^ KW[r,1];
+ C2 = T0[r2&255] ^ T1[(r3>>8)&255] ^ T2[(r0>>16)&255] ^ T3[r1>>24] ^ KW[r,2];
+ C3 = T0[r3&255] ^ T1[(r0>>8)&255] ^ T2[(r1>>16)&255] ^ T3[r2>>24] ^ KW[r++,3];
+ }
+
+ r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[C3>>24] ^ KW[r,0];
+ r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[C0>>24] ^ KW[r,1];
+ r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[C1>>24] ^ KW[r,2];
+ r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[C2>>24] ^ KW[r++,3];
+
+ // the final round's table is a simple function of S so we don't use a whole other four tables for it
+
+ C0 = (uint)S[r0&255] ^ (((uint)S[(r1>>8)&255])<<8) ^ (((uint)S[(r2>>16)&255])<<16) ^ (((uint)S[r3>>24])<<24) ^ KW[r,0];
+ C1 = (uint)S[r1&255] ^ (((uint)S[(r2>>8)&255])<<8) ^ (((uint)S[(r3>>16)&255])<<16) ^ (((uint)S[r0>>24])<<24) ^ KW[r,1];
+ C2 = (uint)S[r2&255] ^ (((uint)S[(r3>>8)&255])<<8) ^ (((uint)S[(r0>>16)&255])<<16) ^ (((uint)S[r1>>24])<<24) ^ KW[r,2];
+ C3 = (uint)S[r3&255] ^ (((uint)S[(r0>>8)&255])<<8) ^ (((uint)S[(r1>>16)&255])<<16) ^ (((uint)S[r2>>24])<<24) ^ KW[r,3];
+ }
+
+ private void DecryptBlock(
+ uint[,] KW)
+ {
+ int r;
+ uint r0, r1, r2, r3;
+
+ C0 ^= KW[ROUNDS,0];
+ C1 ^= KW[ROUNDS,1];
+ C2 ^= KW[ROUNDS,2];
+ C3 ^= KW[ROUNDS,3];
+
+ for (r = ROUNDS-1; r>1;) {
+ r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[C1>>24] ^ KW[r,0];
+ r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[C2>>24] ^ KW[r,1];
+ r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[C3>>24] ^ KW[r,2];
+ r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[C0>>24] ^ KW[r--,3];
+ C0 = Tinv0[r0&255] ^ Tinv1[(r3>>8)&255] ^ Tinv2[(r2>>16)&255] ^ Tinv3[r1>>24] ^ KW[r,0];
+ C1 = Tinv0[r1&255] ^ Tinv1[(r0>>8)&255] ^ Tinv2[(r3>>16)&255] ^ Tinv3[r2>>24] ^ KW[r,1];
+ C2 = Tinv0[r2&255] ^ Tinv1[(r1>>8)&255] ^ Tinv2[(r0>>16)&255] ^ Tinv3[r3>>24] ^ KW[r,2];
+ C3 = Tinv0[r3&255] ^ Tinv1[(r2>>8)&255] ^ Tinv2[(r1>>16)&255] ^ Tinv3[r0>>24] ^ KW[r--,3];
+ }
+
+ r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[C1>>24] ^ KW[r,0];
+ r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[C2>>24] ^ KW[r,1];
+ r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[C3>>24] ^ KW[r,2];
+ r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[C0>>24] ^ KW[r,3];
+
+ // the final round's table is a simple function of Si so we don't use a whole other four tables for it
+
+ C0 = (uint)Si[r0&255] ^ (((uint)Si[(r3>>8)&255])<<8) ^ (((uint)Si[(r2>>16)&255])<<16) ^ (((uint)Si[r1>>24])<<24) ^ KW[0,0];
+ C1 = (uint)Si[r1&255] ^ (((uint)Si[(r0>>8)&255])<<8) ^ (((uint)Si[(r3>>16)&255])<<16) ^ (((uint)Si[r2>>24])<<24) ^ KW[0,1];
+ C2 = (uint)Si[r2&255] ^ (((uint)Si[(r1>>8)&255])<<8) ^ (((uint)Si[(r0>>16)&255])<<16) ^ (((uint)Si[r3>>24])<<24) ^ KW[0,2];
+ C3 = (uint)Si[r3&255] ^ (((uint)Si[(r2>>8)&255])<<8) ^ (((uint)Si[(r1>>16)&255])<<16) ^ (((uint)Si[r0>>24])<<24) ^ KW[0,3];
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/AesLightEngine.cs b/Crypto/src/crypto/engines/AesLightEngine.cs
new file mode 100644
index 000000000..2c495578d
--- /dev/null
+++ b/Crypto/src/crypto/engines/AesLightEngine.cs
@@ -0,0 +1,419 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * an implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ *
+ * There are three levels of tradeoff of speed vs memory
+ * Because java has no preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
+ * and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
+ * adding 12 rotate operations per round to compute the values contained in the other tables from
+ * the contents of the first
+ *
+ * The slowest version uses no static tables at all and computes the values
+ * in each round.
+ * </p>
+ * <p>
+ * This file contains the slowest performance version with no static tables
+ * for round precomputation, but it has the smallest foot print.
+ * </p>
+ */
+ public class AesLightEngine
+ : IBlockCipher
+ {
+ // The S box
+ private static readonly byte[] S =
+ {
+ 99, 124, 119, 123, 242, 107, 111, 197,
+ 48, 1, 103, 43, 254, 215, 171, 118,
+ 202, 130, 201, 125, 250, 89, 71, 240,
+ 173, 212, 162, 175, 156, 164, 114, 192,
+ 183, 253, 147, 38, 54, 63, 247, 204,
+ 52, 165, 229, 241, 113, 216, 49, 21,
+ 4, 199, 35, 195, 24, 150, 5, 154,
+ 7, 18, 128, 226, 235, 39, 178, 117,
+ 9, 131, 44, 26, 27, 110, 90, 160,
+ 82, 59, 214, 179, 41, 227, 47, 132,
+ 83, 209, 0, 237, 32, 252, 177, 91,
+ 106, 203, 190, 57, 74, 76, 88, 207,
+ 208, 239, 170, 251, 67, 77, 51, 133,
+ 69, 249, 2, 127, 80, 60, 159, 168,
+ 81, 163, 64, 143, 146, 157, 56, 245,
+ 188, 182, 218, 33, 16, 255, 243, 210,
+ 205, 12, 19, 236, 95, 151, 68, 23,
+ 196, 167, 126, 61, 100, 93, 25, 115,
+ 96, 129, 79, 220, 34, 42, 144, 136,
+ 70, 238, 184, 20, 222, 94, 11, 219,
+ 224, 50, 58, 10, 73, 6, 36, 92,
+ 194, 211, 172, 98, 145, 149, 228, 121,
+ 231, 200, 55, 109, 141, 213, 78, 169,
+ 108, 86, 244, 234, 101, 122, 174, 8,
+ 186, 120, 37, 46, 28, 166, 180, 198,
+ 232, 221, 116, 31, 75, 189, 139, 138,
+ 112, 62, 181, 102, 72, 3, 246, 14,
+ 97, 53, 87, 185, 134, 193, 29, 158,
+ 225, 248, 152, 17, 105, 217, 142, 148,
+ 155, 30, 135, 233, 206, 85, 40, 223,
+ 140, 161, 137, 13, 191, 230, 66, 104,
+ 65, 153, 45, 15, 176, 84, 187, 22,
+ };
+
+ // The inverse S-box
+ private static readonly byte[] Si =
+ {
+ 82, 9, 106, 213, 48, 54, 165, 56,
+ 191, 64, 163, 158, 129, 243, 215, 251,
+ 124, 227, 57, 130, 155, 47, 255, 135,
+ 52, 142, 67, 68, 196, 222, 233, 203,
+ 84, 123, 148, 50, 166, 194, 35, 61,
+ 238, 76, 149, 11, 66, 250, 195, 78,
+ 8, 46, 161, 102, 40, 217, 36, 178,
+ 118, 91, 162, 73, 109, 139, 209, 37,
+ 114, 248, 246, 100, 134, 104, 152, 22,
+ 212, 164, 92, 204, 93, 101, 182, 146,
+ 108, 112, 72, 80, 253, 237, 185, 218,
+ 94, 21, 70, 87, 167, 141, 157, 132,
+ 144, 216, 171, 0, 140, 188, 211, 10,
+ 247, 228, 88, 5, 184, 179, 69, 6,
+ 208, 44, 30, 143, 202, 63, 15, 2,
+ 193, 175, 189, 3, 1, 19, 138, 107,
+ 58, 145, 17, 65, 79, 103, 220, 234,
+ 151, 242, 207, 206, 240, 180, 230, 115,
+ 150, 172, 116, 34, 231, 173, 53, 133,
+ 226, 249, 55, 232, 28, 117, 223, 110,
+ 71, 241, 26, 113, 29, 41, 197, 137,
+ 111, 183, 98, 14, 170, 24, 190, 27,
+ 252, 86, 62, 75, 198, 210, 121, 32,
+ 154, 219, 192, 254, 120, 205, 90, 244,
+ 31, 221, 168, 51, 136, 7, 199, 49,
+ 177, 18, 16, 89, 39, 128, 236, 95,
+ 96, 81, 127, 169, 25, 181, 74, 13,
+ 45, 229, 122, 159, 147, 201, 156, 239,
+ 160, 224, 59, 77, 174, 42, 245, 176,
+ 200, 235, 187, 60, 131, 83, 153, 97,
+ 23, 43, 4, 126, 186, 119, 214, 38,
+ 225, 105, 20, 99, 85, 33, 12, 125,
+ };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static readonly byte[] rcon =
+ {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91
+ };
+
+ private uint Shift(
+ uint r,
+ int shift)
+ {
+ return (r >> shift) | (r << (32 - shift));
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private const uint m1 = 0x80808080;
+ private const uint m2 = 0x7f7f7f7f;
+ private const uint m3 = 0x0000001b;
+
+ private uint FFmulX(
+ uint x)
+ {
+ return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3);
+ }
+
+ /*
+ The following defines provide alternative definitions of FFmulX that might
+ give improved performance if a fast 32-bit multiply is not available.
+
+ private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+ private static final int m4 = 0x1b1b1b1b;
+ private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+ */
+
+ private uint Mcol(
+ uint x)
+ {
+ uint f2 = FFmulX(x);
+ return f2 ^ Shift(x ^ f2, 8) ^ Shift(x, 16) ^ Shift(x, 24);
+ }
+
+ private uint Inv_Mcol(
+ uint x)
+ {
+ uint f2 = FFmulX(x);
+ uint f4 = FFmulX(f2);
+ uint f8 = FFmulX(f4);
+ uint f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24);
+ }
+
+ private uint SubWord(
+ uint x)
+ {
+ return (uint)S[x&255]
+ | (((uint)S[(x>>8)&255]) << 8)
+ | (((uint)S[(x>>16)&255]) << 16)
+ | (((uint)S[(x>>24)&255]) << 24);
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on key size and block size
+ * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
+ * This code is written assuming those are the only possible values
+ */
+ private uint[,] GenerateWorkingKey(
+ byte[] key,
+ bool forEncryption)
+ {
+ int KC = key.Length / 4; // key length in words
+ int t;
+
+ if ((KC != 4) && (KC != 6) && (KC != 8))
+ throw new ArgumentException("Key length not 128/192/256 bits.");
+
+ ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
+ uint[,] W = new uint[ROUNDS+1,4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ for (int i = 0; i < key.Length; t++)
+ {
+ W[t >> 2, t & 3] = Pack.LE_To_UInt32(key, i);
+ i+=4;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (int i = KC; (i < k); i++)
+ {
+ uint temp = W[(i-1)>>2,(i-1)&3];
+ if ((i % KC) == 0)
+ {
+ temp = SubWord(Shift(temp, 8)) ^ rcon[(i / KC)-1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = SubWord(temp);
+ }
+
+ W[i>>2,i&3] = W[(i - KC)>>2,(i-KC)&3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[j,i] = Inv_Mcol(W[j,i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private uint[,] WorkingKey;
+ private uint C0, C1, C2, C3;
+ private bool forEncryption;
+
+ private const int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AesLightEngine()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().ToString());
+
+ WorkingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey(), forEncryption);
+ this.forEncryption = forEncryption;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "AES"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new InvalidOperationException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ UnPackBlock(input, inOff);
+ EncryptBlock(WorkingKey);
+ PackBlock(output, outOff);
+ }
+ else
+ {
+ UnPackBlock(input, inOff);
+ DecryptBlock(WorkingKey);
+ PackBlock(output, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ }
+
+ private void UnPackBlock(
+ byte[] bytes,
+ int off)
+ {
+ C0 = Pack.LE_To_UInt32(bytes, off);
+ C1 = Pack.LE_To_UInt32(bytes, off + 4);
+ C2 = Pack.LE_To_UInt32(bytes, off + 8);
+ C3 = Pack.LE_To_UInt32(bytes, off + 12);
+ }
+
+ private void PackBlock(
+ byte[] bytes,
+ int off)
+ {
+ Pack.UInt32_To_LE(C0, bytes, off);
+ Pack.UInt32_To_LE(C1, bytes, off + 4);
+ Pack.UInt32_To_LE(C2, bytes, off + 8);
+ Pack.UInt32_To_LE(C3, bytes, off + 12);
+ }
+
+ private void EncryptBlock(
+ uint[,] KW)
+ {
+ int r;
+ uint r0, r1, r2, r3;
+
+ C0 ^= KW[0,0];
+ C1 ^= KW[0,1];
+ C2 ^= KW[0,2];
+ C3 ^= KW[0,3];
+
+ for (r = 1; r < ROUNDS - 1;)
+ {
+ r0 = Mcol((uint)S[C0&255] ^ (((uint)S[(C1>>8)&255])<<8) ^ (((uint)S[(C2>>16)&255])<<16) ^ (((uint)S[(C3>>24)&255])<<24)) ^ KW[r,0];
+ r1 = Mcol((uint)S[C1&255] ^ (((uint)S[(C2>>8)&255])<<8) ^ (((uint)S[(C3>>16)&255])<<16) ^ (((uint)S[(C0>>24)&255])<<24)) ^ KW[r,1];
+ r2 = Mcol((uint)S[C2&255] ^ (((uint)S[(C3>>8)&255])<<8) ^ (((uint)S[(C0>>16)&255])<<16) ^ (((uint)S[(C1>>24)&255])<<24)) ^ KW[r,2];
+ r3 = Mcol((uint)S[C3&255] ^ (((uint)S[(C0>>8)&255])<<8) ^ (((uint)S[(C1>>16)&255])<<16) ^ (((uint)S[(C2>>24)&255])<<24)) ^ KW[r++,3];
+ C0 = Mcol((uint)S[r0&255] ^ (((uint)S[(r1>>8)&255])<<8) ^ (((uint)S[(r2>>16)&255])<<16) ^ (((uint)S[(r3>>24)&255])<<24)) ^ KW[r,0];
+ C1 = Mcol((uint)S[r1&255] ^ (((uint)S[(r2>>8)&255])<<8) ^ (((uint)S[(r3>>16)&255])<<16) ^ (((uint)S[(r0>>24)&255])<<24)) ^ KW[r,1];
+ C2 = Mcol((uint)S[r2&255] ^ (((uint)S[(r3>>8)&255])<<8) ^ (((uint)S[(r0>>16)&255])<<16) ^ (((uint)S[(r1>>24)&255])<<24)) ^ KW[r,2];
+ C3 = Mcol((uint)S[r3&255] ^ (((uint)S[(r0>>8)&255])<<8) ^ (((uint)S[(r1>>16)&255])<<16) ^ (((uint)S[(r2>>24)&255])<<24)) ^ KW[r++,3];
+ }
+
+ r0 = Mcol((uint)S[C0&255] ^ (((uint)S[(C1>>8)&255])<<8) ^ (((uint)S[(C2>>16)&255])<<16) ^ (((uint)S[(C3>>24)&255])<<24)) ^ KW[r,0];
+ r1 = Mcol((uint)S[C1&255] ^ (((uint)S[(C2>>8)&255])<<8) ^ (((uint)S[(C3>>16)&255])<<16) ^ (((uint)S[(C0>>24)&255])<<24)) ^ KW[r,1];
+ r2 = Mcol((uint)S[C2&255] ^ (((uint)S[(C3>>8)&255])<<8) ^ (((uint)S[(C0>>16)&255])<<16) ^ (((uint)S[(C1>>24)&255])<<24)) ^ KW[r,2];
+ r3 = Mcol((uint)S[C3&255] ^ (((uint)S[(C0>>8)&255])<<8) ^ (((uint)S[(C1>>16)&255])<<16) ^ (((uint)S[(C2>>24)&255])<<24)) ^ KW[r++,3];
+
+ // the final round is a simple function of S
+
+ C0 = (uint)S[r0&255] ^ (((uint)S[(r1>>8)&255])<<8) ^ (((uint)S[(r2>>16)&255])<<16) ^ (((uint)S[(r3>>24)&255])<<24) ^ KW[r,0];
+ C1 = (uint)S[r1&255] ^ (((uint)S[(r2>>8)&255])<<8) ^ (((uint)S[(r3>>16)&255])<<16) ^ (((uint)S[(r0>>24)&255])<<24) ^ KW[r,1];
+ C2 = (uint)S[r2&255] ^ (((uint)S[(r3>>8)&255])<<8) ^ (((uint)S[(r0>>16)&255])<<16) ^ (((uint)S[(r1>>24)&255])<<24) ^ KW[r,2];
+ C3 = (uint)S[r3&255] ^ (((uint)S[(r0>>8)&255])<<8) ^ (((uint)S[(r1>>16)&255])<<16) ^ (((uint)S[(r2>>24)&255])<<24) ^ KW[r,3];
+ }
+
+ private void DecryptBlock(
+ uint[,] KW)
+ {
+ int r;
+ uint r0, r1, r2, r3;
+
+ C0 ^= KW[ROUNDS,0];
+ C1 ^= KW[ROUNDS,1];
+ C2 ^= KW[ROUNDS,2];
+ C3 ^= KW[ROUNDS,3];
+
+ for (r = ROUNDS-1; r>1;)
+ {
+ r0 = Inv_Mcol((uint)Si[C0&255] ^ (((uint)Si[(C3>>8)&255])<<8) ^ (((uint)Si[(C2>>16)&255])<<16) ^ ((uint)Si[(C1>>24)&255]<<24)) ^ KW[r,0];
+ r1 = Inv_Mcol((uint)Si[C1&255] ^ (((uint)Si[(C0>>8)&255])<<8) ^ (((uint)Si[(C3>>16)&255])<<16) ^ ((uint)Si[(C2>>24)&255]<<24)) ^ KW[r,1];
+ r2 = Inv_Mcol((uint)Si[C2&255] ^ (((uint)Si[(C1>>8)&255])<<8) ^ (((uint)Si[(C0>>16)&255])<<16) ^ ((uint)Si[(C3>>24)&255]<<24)) ^ KW[r,2];
+ r3 = Inv_Mcol((uint)Si[C3&255] ^ (((uint)Si[(C2>>8)&255])<<8) ^ (((uint)Si[(C1>>16)&255])<<16) ^ ((uint)Si[(C0>>24)&255]<<24)) ^ KW[r--,3];
+ C0 = Inv_Mcol((uint)Si[r0&255] ^ (((uint)Si[(r3>>8)&255])<<8) ^ (((uint)Si[(r2>>16)&255])<<16) ^ ((uint)Si[(r1>>24)&255]<<24)) ^ KW[r,0];
+ C1 = Inv_Mcol((uint)Si[r1&255] ^ (((uint)Si[(r0>>8)&255])<<8) ^ (((uint)Si[(r3>>16)&255])<<16) ^ ((uint)Si[(r2>>24)&255]<<24)) ^ KW[r,1];
+ C2 = Inv_Mcol((uint)Si[r2&255] ^ (((uint)Si[(r1>>8)&255])<<8) ^ (((uint)Si[(r0>>16)&255])<<16) ^ ((uint)Si[(r3>>24)&255]<<24)) ^ KW[r,2];
+ C3 = Inv_Mcol((uint)Si[r3&255] ^ (((uint)Si[(r2>>8)&255])<<8) ^ (((uint)Si[(r1>>16)&255])<<16) ^ ((uint)Si[(r0>>24)&255]<<24)) ^ KW[r--,3];
+ }
+
+ r0 = Inv_Mcol((uint)Si[C0&255] ^ (((uint)Si[(C3>>8)&255])<<8) ^ (((uint)Si[(C2>>16)&255])<<16) ^ ((uint)Si[(C1>>24)&255]<<24)) ^ KW[r,0];
+ r1 = Inv_Mcol((uint)Si[C1&255] ^ (((uint)Si[(C0>>8)&255])<<8) ^ (((uint)Si[(C3>>16)&255])<<16) ^ ((uint)Si[(C2>>24)&255]<<24)) ^ KW[r,1];
+ r2 = Inv_Mcol((uint)Si[C2&255] ^ (((uint)Si[(C1>>8)&255])<<8) ^ (((uint)Si[(C0>>16)&255])<<16) ^ ((uint)Si[(C3>>24)&255]<<24)) ^ KW[r,2];
+ r3 = Inv_Mcol((uint)Si[C3&255] ^ (((uint)Si[(C2>>8)&255])<<8) ^ (((uint)Si[(C1>>16)&255])<<16) ^ ((uint)Si[(C0>>24)&255]<<24)) ^ KW[r,3];
+
+ // the final round's table is a simple function of Si
+
+ C0 = (uint)Si[r0&255] ^ (((uint)Si[(r3>>8)&255])<<8) ^ (((uint)Si[(r2>>16)&255])<<16) ^ (((uint)Si[(r1>>24)&255])<<24) ^ KW[0,0];
+ C1 = (uint)Si[r1&255] ^ (((uint)Si[(r0>>8)&255])<<8) ^ (((uint)Si[(r3>>16)&255])<<16) ^ (((uint)Si[(r2>>24)&255])<<24) ^ KW[0,1];
+ C2 = (uint)Si[r2&255] ^ (((uint)Si[(r1>>8)&255])<<8) ^ (((uint)Si[(r0>>16)&255])<<16) ^ (((uint)Si[(r3>>24)&255])<<24) ^ KW[0,2];
+ C3 = (uint)Si[r3&255] ^ (((uint)Si[(r2>>8)&255])<<8) ^ (((uint)Si[(r1>>16)&255])<<16) ^ (((uint)Si[(r0>>24)&255])<<24) ^ KW[0,3];
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/AesWrapEngine.cs b/Crypto/src/crypto/engines/AesWrapEngine.cs
new file mode 100644
index 000000000..1ce01542b
--- /dev/null
+++ b/Crypto/src/crypto/engines/AesWrapEngine.cs
@@ -0,0 +1,16 @@
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /// <remarks>
+ /// An implementation of the AES Key Wrapper from the NIST Key Wrap Specification.
+ /// <p/>
+ /// For further details see: <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
+ /// </remarks>
+ public class AesWrapEngine
+ : Rfc3394WrapEngine
+ {
+ public AesWrapEngine()
+ : base(new AesEngine())
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/BlowfishEngine.cs b/Crypto/src/crypto/engines/BlowfishEngine.cs
new file mode 100644
index 000000000..8f80f712e
--- /dev/null
+++ b/Crypto/src/crypto/engines/BlowfishEngine.cs
@@ -0,0 +1,561 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * A class that provides Blowfish key encryption operations,
+ * such as encoding data and generating keys.
+ * All the algorithms herein are from Applied Cryptography
+ * and implement a simplified cryptography interface.
+ */
+ public sealed class BlowfishEngine
+ : IBlockCipher
+ {
+ private readonly static uint[] KP =
+ {
+ 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
+ 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
+ 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
+ 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
+ 0x9216D5D9, 0x8979FB1B
+ },
+ KS0 =
+ {
+ 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7,
+ 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
+ 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
+ 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
+ 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE,
+ 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
+ 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF,
+ 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
+ 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
+ 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
+ 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE,
+ 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
+ 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E,
+ 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
+ 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
+ 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
+ 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88,
+ 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
+ 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E,
+ 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
+ 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
+ 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
+ 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88,
+ 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
+ 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6,
+ 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
+ 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
+ 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
+ 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA,
+ 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
+ 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F,
+ 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
+ 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
+ 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
+ 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279,
+ 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
+ 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB,
+ 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
+ 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
+ 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
+ 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0,
+ 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
+ 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790,
+ 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
+ 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
+ 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
+ 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7,
+ 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
+ 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD,
+ 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
+ 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
+ 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
+ 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477,
+ 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
+ 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49,
+ 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
+ 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
+ 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
+ 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41,
+ 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
+ 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
+ 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
+ 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
+ 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
+ },
+ KS1 =
+ {
+ 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623,
+ 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
+ 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
+ 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
+ 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
+ 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
+ 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E,
+ 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
+ 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
+ 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
+ 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
+ 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
+ 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701,
+ 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
+ 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
+ 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
+ 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
+ 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
+ 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E,
+ 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
+ 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
+ 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
+ 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
+ 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
+ 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B,
+ 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
+ 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
+ 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
+ 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
+ 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
+ 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4,
+ 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
+ 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
+ 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
+ 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
+ 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
+ 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510,
+ 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
+ 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
+ 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
+ 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
+ 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
+ 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8,
+ 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
+ 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
+ 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
+ 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
+ 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
+ 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0,
+ 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
+ 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
+ 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
+ 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
+ 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
+ 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00,
+ 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
+ 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
+ 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
+ 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
+ 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
+ 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9,
+ 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
+ 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
+ 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
+ },
+ KS2 =
+ {
+ 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
+ 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
+ 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
+ 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
+ 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45,
+ 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
+ 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
+ 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
+ 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
+ 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
+ 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42,
+ 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
+ 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
+ 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
+ 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
+ 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
+ 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33,
+ 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
+ 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
+ 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
+ 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
+ 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
+ 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B,
+ 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
+ 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
+ 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
+ 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
+ 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
+ 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37,
+ 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
+ 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
+ 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
+ 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
+ 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
+ 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D,
+ 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
+ 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
+ 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
+ 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
+ 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
+ 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D,
+ 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
+ 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
+ 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
+ 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
+ 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
+ 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2,
+ 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
+ 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
+ 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
+ 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
+ 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
+ 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52,
+ 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
+ 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
+ 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
+ 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
+ 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
+ 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24,
+ 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
+ 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
+ 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
+ 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
+ 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
+ },
+ KS3 =
+ {
+ 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B,
+ 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
+ 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
+ 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
+ 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8,
+ 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
+ 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304,
+ 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
+ 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
+ 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
+ 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9,
+ 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
+ 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593,
+ 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
+ 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
+ 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
+ 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B,
+ 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
+ 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C,
+ 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
+ 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
+ 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
+ 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB,
+ 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
+ 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991,
+ 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
+ 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
+ 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
+ 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE,
+ 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
+ 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5,
+ 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
+ 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
+ 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
+ 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84,
+ 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
+ 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8,
+ 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
+ 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
+ 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
+ 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38,
+ 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
+ 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C,
+ 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
+ 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
+ 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
+ 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964,
+ 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
+ 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8,
+ 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
+ 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
+ 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
+ 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02,
+ 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
+ 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614,
+ 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
+ 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
+ 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
+ 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0,
+ 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
+ 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E,
+ 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
+ 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
+ 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
+ };
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ private static readonly int ROUNDS = 16;
+ private const int BLOCK_SIZE = 8; // bytes = 64 bits
+ private static readonly int SBOX_SK = 256;
+ private static readonly int P_SZ = ROUNDS+2;
+
+ private readonly uint[] S0, S1, S2, S3; // the s-boxes
+ private readonly uint[] P; // the p-array
+
+ private bool encrypting;
+
+ private byte[] workingKey;
+
+ public BlowfishEngine()
+ {
+ S0 = new uint[SBOX_SK];
+ S1 = new uint[SBOX_SK];
+ S2 = new uint[SBOX_SK];
+ S3 = new uint[SBOX_SK];
+ P = new uint[P_SZ];
+ }
+
+ /**
+ * initialise a Blowfish cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("invalid parameter passed to Blowfish init - " + parameters.GetType().ToString());
+
+ this.encrypting = forEncryption;
+ this.workingKey = ((KeyParameter)parameters).GetKey();
+ SetKey(this.workingKey);
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Blowfish"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new InvalidOperationException("Blowfish not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ EncryptBlock(input, inOff, output, outOff);
+ }
+ else
+ {
+ DecryptBlock(input, inOff, output, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ private uint F(uint x)
+ {
+ return (((S0[x >> 24] + S1[(x >> 16) & 0xff]) ^ S2[(x >> 8) & 0xff]) + S3[x & 0xff]);
+ }
+
+ /**
+ * apply the encryption cycle to each value pair in the table.
+ */
+ private void ProcessTable(
+ uint xl,
+ uint xr,
+ uint[] table)
+ {
+ int size = table.Length;
+
+ for (int s = 0; s < size; s += 2)
+ {
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ table[s] = xr;
+ table[s + 1] = xl;
+
+ xr = xl; // end of cycle swap
+ xl = table[s];
+ }
+ }
+
+ private void SetKey(byte[] key)
+ {
+ /*
+ * - comments are from _Applied Crypto_, Schneier, p338
+ * please be careful comparing the two, AC numbers the
+ * arrays from 1, the enclosed code from 0.
+ *
+ * (1)
+ * Initialise the S-boxes and the P-array, with a fixed string
+ * This string contains the hexadecimal digits of pi (3.141...)
+ */
+ Array.Copy(KS0, 0, S0, 0, SBOX_SK);
+ Array.Copy(KS1, 0, S1, 0, SBOX_SK);
+ Array.Copy(KS2, 0, S2, 0, SBOX_SK);
+ Array.Copy(KS3, 0, S3, 0, SBOX_SK);
+
+ Array.Copy(KP, 0, P, 0, P_SZ);
+
+ /*
+ * (2)
+ * Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with the
+ * second 32-bits of the key, and so on for all bits of the key
+ * (up to P[17]). Repeatedly cycle through the key bits until the
+ * entire P-array has been XOR-ed with the key bits
+ */
+ int keyLength = key.Length;
+ int keyIndex = 0;
+
+ for (int i=0; i < P_SZ; i++)
+ {
+ // Get the 32 bits of the key, in 4 * 8 bit chunks
+ uint data = 0x0000000;
+ for (int j=0; j < 4; j++)
+ {
+ // create a 32 bit block
+ data = (data << 8) | (uint)key[keyIndex++];
+
+ // wrap when we get to the end of the key
+ if (keyIndex >= keyLength)
+ {
+ keyIndex = 0;
+ }
+ }
+ // XOR the newly created 32 bit chunk onto the P-array
+ P[i] ^= data;
+ }
+
+ /*
+ * (3)
+ * Encrypt the all-zero string with the Blowfish algorithm, using
+ * the subkeys described in (1) and (2)
+ *
+ * (4)
+ * Replace P1 and P2 with the output of step (3)
+ *
+ * (5)
+ * Encrypt the output of step(3) using the Blowfish algorithm,
+ * with the modified subkeys.
+ *
+ * (6)
+ * Replace P3 and P4 with the output of step (5)
+ *
+ * (7)
+ * Continue the process, replacing all elements of the P-array
+ * and then all four S-boxes in order, with the output of the
+ * continuously changing Blowfish algorithm
+ */
+
+ ProcessTable(0, 0, P);
+ ProcessTable(P[P_SZ - 2], P[P_SZ - 1], S0);
+ ProcessTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1);
+ ProcessTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2);
+ ProcessTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ */
+ private void EncryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ uint xl = Pack.BE_To_UInt32(src, srcIndex);
+ uint xr = Pack.BE_To_UInt32(src, srcIndex+4);
+
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ Pack.UInt32_To_BE(xr, dst, dstIndex);
+ Pack.UInt32_To_BE(xl, dst, dstIndex + 4);
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ */
+ private void DecryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ uint xl = Pack.BE_To_UInt32(src, srcIndex);
+ uint xr = Pack.BE_To_UInt32(src, srcIndex + 4);
+
+ xl ^= P[ROUNDS + 1];
+
+ for (int i = ROUNDS; i > 0 ; i -= 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i - 1];
+ }
+
+ xr ^= P[0];
+
+ Pack.UInt32_To_BE(xr, dst, dstIndex);
+ Pack.UInt32_To_BE(xl, dst, dstIndex + 4);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/CamelliaEngine.cs b/Crypto/src/crypto/engines/CamelliaEngine.cs
new file mode 100644
index 000000000..8f4a442e9
--- /dev/null
+++ b/Crypto/src/crypto/engines/CamelliaEngine.cs
@@ -0,0 +1,669 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * Camellia - based on RFC 3713.
+ */
+ public class CamelliaEngine
+ : IBlockCipher
+ {
+ private bool initialised = false;
+ private bool _keyIs128;
+
+ private const int BLOCK_SIZE = 16;
+
+ private uint[] subkey = new uint[24 * 4];
+ private uint[] kw = new uint[4 * 2]; // for whitening
+ private uint[] ke = new uint[6 * 2]; // for FL and FL^(-1)
+ private uint[] state = new uint[4]; // for encryption and decryption
+
+ private static readonly uint[] SIGMA = new uint[]{
+ 0xa09e667f, 0x3bcc908b,
+ 0xb67ae858, 0x4caa73b2,
+ 0xc6ef372f, 0xe94f82be,
+ 0x54ff53a5, 0xf1d36f1c,
+ 0x10e527fa, 0xde682d1d,
+ 0xb05688c2, 0xb3e6c1fd
+ };
+
+ /*
+ *
+ * S-box data
+ *
+ */
+ private static readonly uint[] SBOX1_1110 = new uint[]{
+ 0x70707000, 0x82828200, 0x2c2c2c00, 0xececec00, 0xb3b3b300, 0x27272700,
+ 0xc0c0c000, 0xe5e5e500, 0xe4e4e400, 0x85858500, 0x57575700, 0x35353500,
+ 0xeaeaea00, 0x0c0c0c00, 0xaeaeae00, 0x41414100, 0x23232300, 0xefefef00,
+ 0x6b6b6b00, 0x93939300, 0x45454500, 0x19191900, 0xa5a5a500, 0x21212100,
+ 0xededed00, 0x0e0e0e00, 0x4f4f4f00, 0x4e4e4e00, 0x1d1d1d00, 0x65656500,
+ 0x92929200, 0xbdbdbd00, 0x86868600, 0xb8b8b800, 0xafafaf00, 0x8f8f8f00,
+ 0x7c7c7c00, 0xebebeb00, 0x1f1f1f00, 0xcecece00, 0x3e3e3e00, 0x30303000,
+ 0xdcdcdc00, 0x5f5f5f00, 0x5e5e5e00, 0xc5c5c500, 0x0b0b0b00, 0x1a1a1a00,
+ 0xa6a6a600, 0xe1e1e100, 0x39393900, 0xcacaca00, 0xd5d5d500, 0x47474700,
+ 0x5d5d5d00, 0x3d3d3d00, 0xd9d9d900, 0x01010100, 0x5a5a5a00, 0xd6d6d600,
+ 0x51515100, 0x56565600, 0x6c6c6c00, 0x4d4d4d00, 0x8b8b8b00, 0x0d0d0d00,
+ 0x9a9a9a00, 0x66666600, 0xfbfbfb00, 0xcccccc00, 0xb0b0b000, 0x2d2d2d00,
+ 0x74747400, 0x12121200, 0x2b2b2b00, 0x20202000, 0xf0f0f000, 0xb1b1b100,
+ 0x84848400, 0x99999900, 0xdfdfdf00, 0x4c4c4c00, 0xcbcbcb00, 0xc2c2c200,
+ 0x34343400, 0x7e7e7e00, 0x76767600, 0x05050500, 0x6d6d6d00, 0xb7b7b700,
+ 0xa9a9a900, 0x31313100, 0xd1d1d100, 0x17171700, 0x04040400, 0xd7d7d700,
+ 0x14141400, 0x58585800, 0x3a3a3a00, 0x61616100, 0xdedede00, 0x1b1b1b00,
+ 0x11111100, 0x1c1c1c00, 0x32323200, 0x0f0f0f00, 0x9c9c9c00, 0x16161600,
+ 0x53535300, 0x18181800, 0xf2f2f200, 0x22222200, 0xfefefe00, 0x44444400,
+ 0xcfcfcf00, 0xb2b2b200, 0xc3c3c300, 0xb5b5b500, 0x7a7a7a00, 0x91919100,
+ 0x24242400, 0x08080800, 0xe8e8e800, 0xa8a8a800, 0x60606000, 0xfcfcfc00,
+ 0x69696900, 0x50505000, 0xaaaaaa00, 0xd0d0d000, 0xa0a0a000, 0x7d7d7d00,
+ 0xa1a1a100, 0x89898900, 0x62626200, 0x97979700, 0x54545400, 0x5b5b5b00,
+ 0x1e1e1e00, 0x95959500, 0xe0e0e000, 0xffffff00, 0x64646400, 0xd2d2d200,
+ 0x10101000, 0xc4c4c400, 0x00000000, 0x48484800, 0xa3a3a300, 0xf7f7f700,
+ 0x75757500, 0xdbdbdb00, 0x8a8a8a00, 0x03030300, 0xe6e6e600, 0xdadada00,
+ 0x09090900, 0x3f3f3f00, 0xdddddd00, 0x94949400, 0x87878700, 0x5c5c5c00,
+ 0x83838300, 0x02020200, 0xcdcdcd00, 0x4a4a4a00, 0x90909000, 0x33333300,
+ 0x73737300, 0x67676700, 0xf6f6f600, 0xf3f3f300, 0x9d9d9d00, 0x7f7f7f00,
+ 0xbfbfbf00, 0xe2e2e200, 0x52525200, 0x9b9b9b00, 0xd8d8d800, 0x26262600,
+ 0xc8c8c800, 0x37373700, 0xc6c6c600, 0x3b3b3b00, 0x81818100, 0x96969600,
+ 0x6f6f6f00, 0x4b4b4b00, 0x13131300, 0xbebebe00, 0x63636300, 0x2e2e2e00,
+ 0xe9e9e900, 0x79797900, 0xa7a7a700, 0x8c8c8c00, 0x9f9f9f00, 0x6e6e6e00,
+ 0xbcbcbc00, 0x8e8e8e00, 0x29292900, 0xf5f5f500, 0xf9f9f900, 0xb6b6b600,
+ 0x2f2f2f00, 0xfdfdfd00, 0xb4b4b400, 0x59595900, 0x78787800, 0x98989800,
+ 0x06060600, 0x6a6a6a00, 0xe7e7e700, 0x46464600, 0x71717100, 0xbababa00,
+ 0xd4d4d400, 0x25252500, 0xababab00, 0x42424200, 0x88888800, 0xa2a2a200,
+ 0x8d8d8d00, 0xfafafa00, 0x72727200, 0x07070700, 0xb9b9b900, 0x55555500,
+ 0xf8f8f800, 0xeeeeee00, 0xacacac00, 0x0a0a0a00, 0x36363600, 0x49494900,
+ 0x2a2a2a00, 0x68686800, 0x3c3c3c00, 0x38383800, 0xf1f1f100, 0xa4a4a400,
+ 0x40404000, 0x28282800, 0xd3d3d300, 0x7b7b7b00, 0xbbbbbb00, 0xc9c9c900,
+ 0x43434300, 0xc1c1c100, 0x15151500, 0xe3e3e300, 0xadadad00, 0xf4f4f400,
+ 0x77777700, 0xc7c7c700, 0x80808000, 0x9e9e9e00
+ };
+
+ private static readonly uint[] SBOX4_4404 = new uint[]{
+ 0x70700070, 0x2c2c002c, 0xb3b300b3, 0xc0c000c0, 0xe4e400e4, 0x57570057,
+ 0xeaea00ea, 0xaeae00ae, 0x23230023, 0x6b6b006b, 0x45450045, 0xa5a500a5,
+ 0xeded00ed, 0x4f4f004f, 0x1d1d001d, 0x92920092, 0x86860086, 0xafaf00af,
+ 0x7c7c007c, 0x1f1f001f, 0x3e3e003e, 0xdcdc00dc, 0x5e5e005e, 0x0b0b000b,
+ 0xa6a600a6, 0x39390039, 0xd5d500d5, 0x5d5d005d, 0xd9d900d9, 0x5a5a005a,
+ 0x51510051, 0x6c6c006c, 0x8b8b008b, 0x9a9a009a, 0xfbfb00fb, 0xb0b000b0,
+ 0x74740074, 0x2b2b002b, 0xf0f000f0, 0x84840084, 0xdfdf00df, 0xcbcb00cb,
+ 0x34340034, 0x76760076, 0x6d6d006d, 0xa9a900a9, 0xd1d100d1, 0x04040004,
+ 0x14140014, 0x3a3a003a, 0xdede00de, 0x11110011, 0x32320032, 0x9c9c009c,
+ 0x53530053, 0xf2f200f2, 0xfefe00fe, 0xcfcf00cf, 0xc3c300c3, 0x7a7a007a,
+ 0x24240024, 0xe8e800e8, 0x60600060, 0x69690069, 0xaaaa00aa, 0xa0a000a0,
+ 0xa1a100a1, 0x62620062, 0x54540054, 0x1e1e001e, 0xe0e000e0, 0x64640064,
+ 0x10100010, 0x00000000, 0xa3a300a3, 0x75750075, 0x8a8a008a, 0xe6e600e6,
+ 0x09090009, 0xdddd00dd, 0x87870087, 0x83830083, 0xcdcd00cd, 0x90900090,
+ 0x73730073, 0xf6f600f6, 0x9d9d009d, 0xbfbf00bf, 0x52520052, 0xd8d800d8,
+ 0xc8c800c8, 0xc6c600c6, 0x81810081, 0x6f6f006f, 0x13130013, 0x63630063,
+ 0xe9e900e9, 0xa7a700a7, 0x9f9f009f, 0xbcbc00bc, 0x29290029, 0xf9f900f9,
+ 0x2f2f002f, 0xb4b400b4, 0x78780078, 0x06060006, 0xe7e700e7, 0x71710071,
+ 0xd4d400d4, 0xabab00ab, 0x88880088, 0x8d8d008d, 0x72720072, 0xb9b900b9,
+ 0xf8f800f8, 0xacac00ac, 0x36360036, 0x2a2a002a, 0x3c3c003c, 0xf1f100f1,
+ 0x40400040, 0xd3d300d3, 0xbbbb00bb, 0x43430043, 0x15150015, 0xadad00ad,
+ 0x77770077, 0x80800080, 0x82820082, 0xecec00ec, 0x27270027, 0xe5e500e5,
+ 0x85850085, 0x35350035, 0x0c0c000c, 0x41410041, 0xefef00ef, 0x93930093,
+ 0x19190019, 0x21210021, 0x0e0e000e, 0x4e4e004e, 0x65650065, 0xbdbd00bd,
+ 0xb8b800b8, 0x8f8f008f, 0xebeb00eb, 0xcece00ce, 0x30300030, 0x5f5f005f,
+ 0xc5c500c5, 0x1a1a001a, 0xe1e100e1, 0xcaca00ca, 0x47470047, 0x3d3d003d,
+ 0x01010001, 0xd6d600d6, 0x56560056, 0x4d4d004d, 0x0d0d000d, 0x66660066,
+ 0xcccc00cc, 0x2d2d002d, 0x12120012, 0x20200020, 0xb1b100b1, 0x99990099,
+ 0x4c4c004c, 0xc2c200c2, 0x7e7e007e, 0x05050005, 0xb7b700b7, 0x31310031,
+ 0x17170017, 0xd7d700d7, 0x58580058, 0x61610061, 0x1b1b001b, 0x1c1c001c,
+ 0x0f0f000f, 0x16160016, 0x18180018, 0x22220022, 0x44440044, 0xb2b200b2,
+ 0xb5b500b5, 0x91910091, 0x08080008, 0xa8a800a8, 0xfcfc00fc, 0x50500050,
+ 0xd0d000d0, 0x7d7d007d, 0x89890089, 0x97970097, 0x5b5b005b, 0x95950095,
+ 0xffff00ff, 0xd2d200d2, 0xc4c400c4, 0x48480048, 0xf7f700f7, 0xdbdb00db,
+ 0x03030003, 0xdada00da, 0x3f3f003f, 0x94940094, 0x5c5c005c, 0x02020002,
+ 0x4a4a004a, 0x33330033, 0x67670067, 0xf3f300f3, 0x7f7f007f, 0xe2e200e2,
+ 0x9b9b009b, 0x26260026, 0x37370037, 0x3b3b003b, 0x96960096, 0x4b4b004b,
+ 0xbebe00be, 0x2e2e002e, 0x79790079, 0x8c8c008c, 0x6e6e006e, 0x8e8e008e,
+ 0xf5f500f5, 0xb6b600b6, 0xfdfd00fd, 0x59590059, 0x98980098, 0x6a6a006a,
+ 0x46460046, 0xbaba00ba, 0x25250025, 0x42420042, 0xa2a200a2, 0xfafa00fa,
+ 0x07070007, 0x55550055, 0xeeee00ee, 0x0a0a000a, 0x49490049, 0x68680068,
+ 0x38380038, 0xa4a400a4, 0x28280028, 0x7b7b007b, 0xc9c900c9, 0xc1c100c1,
+ 0xe3e300e3, 0xf4f400f4, 0xc7c700c7, 0x9e9e009e
+ };
+
+ private static readonly uint[] SBOX2_0222 = new uint[]{
+ 0x00e0e0e0, 0x00050505, 0x00585858, 0x00d9d9d9, 0x00676767, 0x004e4e4e,
+ 0x00818181, 0x00cbcbcb, 0x00c9c9c9, 0x000b0b0b, 0x00aeaeae, 0x006a6a6a,
+ 0x00d5d5d5, 0x00181818, 0x005d5d5d, 0x00828282, 0x00464646, 0x00dfdfdf,
+ 0x00d6d6d6, 0x00272727, 0x008a8a8a, 0x00323232, 0x004b4b4b, 0x00424242,
+ 0x00dbdbdb, 0x001c1c1c, 0x009e9e9e, 0x009c9c9c, 0x003a3a3a, 0x00cacaca,
+ 0x00252525, 0x007b7b7b, 0x000d0d0d, 0x00717171, 0x005f5f5f, 0x001f1f1f,
+ 0x00f8f8f8, 0x00d7d7d7, 0x003e3e3e, 0x009d9d9d, 0x007c7c7c, 0x00606060,
+ 0x00b9b9b9, 0x00bebebe, 0x00bcbcbc, 0x008b8b8b, 0x00161616, 0x00343434,
+ 0x004d4d4d, 0x00c3c3c3, 0x00727272, 0x00959595, 0x00ababab, 0x008e8e8e,
+ 0x00bababa, 0x007a7a7a, 0x00b3b3b3, 0x00020202, 0x00b4b4b4, 0x00adadad,
+ 0x00a2a2a2, 0x00acacac, 0x00d8d8d8, 0x009a9a9a, 0x00171717, 0x001a1a1a,
+ 0x00353535, 0x00cccccc, 0x00f7f7f7, 0x00999999, 0x00616161, 0x005a5a5a,
+ 0x00e8e8e8, 0x00242424, 0x00565656, 0x00404040, 0x00e1e1e1, 0x00636363,
+ 0x00090909, 0x00333333, 0x00bfbfbf, 0x00989898, 0x00979797, 0x00858585,
+ 0x00686868, 0x00fcfcfc, 0x00ececec, 0x000a0a0a, 0x00dadada, 0x006f6f6f,
+ 0x00535353, 0x00626262, 0x00a3a3a3, 0x002e2e2e, 0x00080808, 0x00afafaf,
+ 0x00282828, 0x00b0b0b0, 0x00747474, 0x00c2c2c2, 0x00bdbdbd, 0x00363636,
+ 0x00222222, 0x00383838, 0x00646464, 0x001e1e1e, 0x00393939, 0x002c2c2c,
+ 0x00a6a6a6, 0x00303030, 0x00e5e5e5, 0x00444444, 0x00fdfdfd, 0x00888888,
+ 0x009f9f9f, 0x00656565, 0x00878787, 0x006b6b6b, 0x00f4f4f4, 0x00232323,
+ 0x00484848, 0x00101010, 0x00d1d1d1, 0x00515151, 0x00c0c0c0, 0x00f9f9f9,
+ 0x00d2d2d2, 0x00a0a0a0, 0x00555555, 0x00a1a1a1, 0x00414141, 0x00fafafa,
+ 0x00434343, 0x00131313, 0x00c4c4c4, 0x002f2f2f, 0x00a8a8a8, 0x00b6b6b6,
+ 0x003c3c3c, 0x002b2b2b, 0x00c1c1c1, 0x00ffffff, 0x00c8c8c8, 0x00a5a5a5,
+ 0x00202020, 0x00898989, 0x00000000, 0x00909090, 0x00474747, 0x00efefef,
+ 0x00eaeaea, 0x00b7b7b7, 0x00151515, 0x00060606, 0x00cdcdcd, 0x00b5b5b5,
+ 0x00121212, 0x007e7e7e, 0x00bbbbbb, 0x00292929, 0x000f0f0f, 0x00b8b8b8,
+ 0x00070707, 0x00040404, 0x009b9b9b, 0x00949494, 0x00212121, 0x00666666,
+ 0x00e6e6e6, 0x00cecece, 0x00ededed, 0x00e7e7e7, 0x003b3b3b, 0x00fefefe,
+ 0x007f7f7f, 0x00c5c5c5, 0x00a4a4a4, 0x00373737, 0x00b1b1b1, 0x004c4c4c,
+ 0x00919191, 0x006e6e6e, 0x008d8d8d, 0x00767676, 0x00030303, 0x002d2d2d,
+ 0x00dedede, 0x00969696, 0x00262626, 0x007d7d7d, 0x00c6c6c6, 0x005c5c5c,
+ 0x00d3d3d3, 0x00f2f2f2, 0x004f4f4f, 0x00191919, 0x003f3f3f, 0x00dcdcdc,
+ 0x00797979, 0x001d1d1d, 0x00525252, 0x00ebebeb, 0x00f3f3f3, 0x006d6d6d,
+ 0x005e5e5e, 0x00fbfbfb, 0x00696969, 0x00b2b2b2, 0x00f0f0f0, 0x00313131,
+ 0x000c0c0c, 0x00d4d4d4, 0x00cfcfcf, 0x008c8c8c, 0x00e2e2e2, 0x00757575,
+ 0x00a9a9a9, 0x004a4a4a, 0x00575757, 0x00848484, 0x00111111, 0x00454545,
+ 0x001b1b1b, 0x00f5f5f5, 0x00e4e4e4, 0x000e0e0e, 0x00737373, 0x00aaaaaa,
+ 0x00f1f1f1, 0x00dddddd, 0x00595959, 0x00141414, 0x006c6c6c, 0x00929292,
+ 0x00545454, 0x00d0d0d0, 0x00787878, 0x00707070, 0x00e3e3e3, 0x00494949,
+ 0x00808080, 0x00505050, 0x00a7a7a7, 0x00f6f6f6, 0x00777777, 0x00939393,
+ 0x00868686, 0x00838383, 0x002a2a2a, 0x00c7c7c7, 0x005b5b5b, 0x00e9e9e9,
+ 0x00eeeeee, 0x008f8f8f, 0x00010101, 0x003d3d3d
+ };
+
+ private static readonly uint[] SBOX3_3033 = new uint[]{
+ 0x38003838, 0x41004141, 0x16001616, 0x76007676, 0xd900d9d9, 0x93009393,
+ 0x60006060, 0xf200f2f2, 0x72007272, 0xc200c2c2, 0xab00abab, 0x9a009a9a,
+ 0x75007575, 0x06000606, 0x57005757, 0xa000a0a0, 0x91009191, 0xf700f7f7,
+ 0xb500b5b5, 0xc900c9c9, 0xa200a2a2, 0x8c008c8c, 0xd200d2d2, 0x90009090,
+ 0xf600f6f6, 0x07000707, 0xa700a7a7, 0x27002727, 0x8e008e8e, 0xb200b2b2,
+ 0x49004949, 0xde00dede, 0x43004343, 0x5c005c5c, 0xd700d7d7, 0xc700c7c7,
+ 0x3e003e3e, 0xf500f5f5, 0x8f008f8f, 0x67006767, 0x1f001f1f, 0x18001818,
+ 0x6e006e6e, 0xaf00afaf, 0x2f002f2f, 0xe200e2e2, 0x85008585, 0x0d000d0d,
+ 0x53005353, 0xf000f0f0, 0x9c009c9c, 0x65006565, 0xea00eaea, 0xa300a3a3,
+ 0xae00aeae, 0x9e009e9e, 0xec00ecec, 0x80008080, 0x2d002d2d, 0x6b006b6b,
+ 0xa800a8a8, 0x2b002b2b, 0x36003636, 0xa600a6a6, 0xc500c5c5, 0x86008686,
+ 0x4d004d4d, 0x33003333, 0xfd00fdfd, 0x66006666, 0x58005858, 0x96009696,
+ 0x3a003a3a, 0x09000909, 0x95009595, 0x10001010, 0x78007878, 0xd800d8d8,
+ 0x42004242, 0xcc00cccc, 0xef00efef, 0x26002626, 0xe500e5e5, 0x61006161,
+ 0x1a001a1a, 0x3f003f3f, 0x3b003b3b, 0x82008282, 0xb600b6b6, 0xdb00dbdb,
+ 0xd400d4d4, 0x98009898, 0xe800e8e8, 0x8b008b8b, 0x02000202, 0xeb00ebeb,
+ 0x0a000a0a, 0x2c002c2c, 0x1d001d1d, 0xb000b0b0, 0x6f006f6f, 0x8d008d8d,
+ 0x88008888, 0x0e000e0e, 0x19001919, 0x87008787, 0x4e004e4e, 0x0b000b0b,
+ 0xa900a9a9, 0x0c000c0c, 0x79007979, 0x11001111, 0x7f007f7f, 0x22002222,
+ 0xe700e7e7, 0x59005959, 0xe100e1e1, 0xda00dada, 0x3d003d3d, 0xc800c8c8,
+ 0x12001212, 0x04000404, 0x74007474, 0x54005454, 0x30003030, 0x7e007e7e,
+ 0xb400b4b4, 0x28002828, 0x55005555, 0x68006868, 0x50005050, 0xbe00bebe,
+ 0xd000d0d0, 0xc400c4c4, 0x31003131, 0xcb00cbcb, 0x2a002a2a, 0xad00adad,
+ 0x0f000f0f, 0xca00caca, 0x70007070, 0xff00ffff, 0x32003232, 0x69006969,
+ 0x08000808, 0x62006262, 0x00000000, 0x24002424, 0xd100d1d1, 0xfb00fbfb,
+ 0xba00baba, 0xed00eded, 0x45004545, 0x81008181, 0x73007373, 0x6d006d6d,
+ 0x84008484, 0x9f009f9f, 0xee00eeee, 0x4a004a4a, 0xc300c3c3, 0x2e002e2e,
+ 0xc100c1c1, 0x01000101, 0xe600e6e6, 0x25002525, 0x48004848, 0x99009999,
+ 0xb900b9b9, 0xb300b3b3, 0x7b007b7b, 0xf900f9f9, 0xce00cece, 0xbf00bfbf,
+ 0xdf00dfdf, 0x71007171, 0x29002929, 0xcd00cdcd, 0x6c006c6c, 0x13001313,
+ 0x64006464, 0x9b009b9b, 0x63006363, 0x9d009d9d, 0xc000c0c0, 0x4b004b4b,
+ 0xb700b7b7, 0xa500a5a5, 0x89008989, 0x5f005f5f, 0xb100b1b1, 0x17001717,
+ 0xf400f4f4, 0xbc00bcbc, 0xd300d3d3, 0x46004646, 0xcf00cfcf, 0x37003737,
+ 0x5e005e5e, 0x47004747, 0x94009494, 0xfa00fafa, 0xfc00fcfc, 0x5b005b5b,
+ 0x97009797, 0xfe00fefe, 0x5a005a5a, 0xac00acac, 0x3c003c3c, 0x4c004c4c,
+ 0x03000303, 0x35003535, 0xf300f3f3, 0x23002323, 0xb800b8b8, 0x5d005d5d,
+ 0x6a006a6a, 0x92009292, 0xd500d5d5, 0x21002121, 0x44004444, 0x51005151,
+ 0xc600c6c6, 0x7d007d7d, 0x39003939, 0x83008383, 0xdc00dcdc, 0xaa00aaaa,
+ 0x7c007c7c, 0x77007777, 0x56005656, 0x05000505, 0x1b001b1b, 0xa400a4a4,
+ 0x15001515, 0x34003434, 0x1e001e1e, 0x1c001c1c, 0xf800f8f8, 0x52005252,
+ 0x20002020, 0x14001414, 0xe900e9e9, 0xbd00bdbd, 0xdd00dddd, 0xe400e4e4,
+ 0xa100a1a1, 0xe000e0e0, 0x8a008a8a, 0xf100f1f1, 0xd600d6d6, 0x7a007a7a,
+ 0xbb00bbbb, 0xe300e3e3, 0x40004040, 0x4f004f4f
+ };
+
+ private static uint rightRotate(uint x, int s)
+ {
+ return ((x >> s) + (x << (32 - s)));
+ }
+
+ private static uint leftRotate(uint x, int s)
+ {
+ return (x << s) + (x >> (32 - s));
+ }
+
+ private static void roldq(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >> (32 - rot));
+ ko[1 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >> (32 - rot));
+ ko[2 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >> (32 - rot));
+ ko[3 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >> (32 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldq(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >> (32 - rot));
+ ko[3 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >> (32 - rot));
+ ko[0 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >> (32 - rot));
+ ko[1 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >> (32 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private static void roldqo32(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >> (64 - rot));
+ ko[1 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >> (64 - rot));
+ ko[2 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >> (64 - rot));
+ ko[3 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >> (64 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldqo32(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >> (64 - rot));
+ ko[3 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >> (64 - rot));
+ ko[0 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >> (64 - rot));
+ ko[1 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >> (64 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private static uint bytes2uint(byte[] src, int offset)
+ {
+ uint word = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ word = (word << 8) + (uint)src[i + offset];
+ }
+ return word;
+ }
+
+ private static void uint2bytes(uint word, byte[] dst, int offset)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ dst[(3 - i) + offset] = (byte)word;
+ word >>= 8;
+ }
+ }
+
+ private static void camelliaF2(uint[] s, uint[] skey, int keyoff)
+ {
+ uint t1, t2, u, v;
+
+ t1 = s[0] ^ skey[0 + keyoff];
+ u = SBOX4_4404[(byte)t1];
+ u ^= SBOX3_3033[(byte)(t1 >> 8)];
+ u ^= SBOX2_0222[(byte)(t1 >> 16)];
+ u ^= SBOX1_1110[(byte)(t1 >> 24)];
+ t2 = s[1] ^ skey[1 + keyoff];
+ v = SBOX1_1110[(byte)t2];
+ v ^= SBOX4_4404[(byte)(t2 >> 8)];
+ v ^= SBOX3_3033[(byte)(t2 >> 16)];
+ v ^= SBOX2_0222[(byte)(t2 >> 24)];
+
+ s[2] ^= u ^ v;
+ s[3] ^= u ^ v ^ rightRotate(u, 8);
+
+ t1 = s[2] ^ skey[2 + keyoff];
+ u = SBOX4_4404[(byte)t1];
+ u ^= SBOX3_3033[(byte)(t1 >> 8)];
+ u ^= SBOX2_0222[(byte)(t1 >> 16)];
+ u ^= SBOX1_1110[(byte)(t1 >> 24)];
+ t2 = s[3] ^ skey[3 + keyoff];
+ v = SBOX1_1110[(byte)t2];
+ v ^= SBOX4_4404[(byte)(t2 >> 8)];
+ v ^= SBOX3_3033[(byte)(t2 >> 16)];
+ v ^= SBOX2_0222[(byte)(t2 >> 24)];
+
+ s[0] ^= u ^ v;
+ s[1] ^= u ^ v ^ rightRotate(u, 8);
+ }
+
+ private static void camelliaFLs(uint[] s, uint[] fkey, int keyoff)
+ {
+
+ s[1] ^= leftRotate(s[0] & fkey[0 + keyoff], 1);
+ s[0] ^= fkey[1 + keyoff] | s[1];
+
+ s[2] ^= fkey[3 + keyoff] | s[3];
+ s[3] ^= leftRotate(fkey[2 + keyoff] & s[2], 1);
+ }
+
+ private void setKey(bool forEncryption, byte[] key)
+ {
+ uint[] k = new uint[8];
+ uint[] ka = new uint[4];
+ uint[] kb = new uint[4];
+ uint[] t = new uint[4];
+
+ switch (key.Length)
+ {
+ case 16:
+ _keyIs128 = true;
+ k[0] = bytes2uint(key, 0);
+ k[1] = bytes2uint(key, 4);
+ k[2] = bytes2uint(key, 8);
+ k[3] = bytes2uint(key, 12);
+ k[4] = k[5] = k[6] = k[7] = 0;
+ break;
+ case 24:
+ k[0] = bytes2uint(key, 0);
+ k[1] = bytes2uint(key, 4);
+ k[2] = bytes2uint(key, 8);
+ k[3] = bytes2uint(key, 12);
+ k[4] = bytes2uint(key, 16);
+ k[5] = bytes2uint(key, 20);
+ k[6] = ~k[4];
+ k[7] = ~k[5];
+ _keyIs128 = false;
+ break;
+ case 32:
+ k[0] = bytes2uint(key, 0);
+ k[1] = bytes2uint(key, 4);
+ k[2] = bytes2uint(key, 8);
+ k[3] = bytes2uint(key, 12);
+ k[4] = bytes2uint(key, 16);
+ k[5] = bytes2uint(key, 20);
+ k[6] = bytes2uint(key, 24);
+ k[7] = bytes2uint(key, 28);
+ _keyIs128 = false;
+ break;
+ default:
+ throw new ArgumentException("key sizes are only 16/24/32 bytes.");
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] = k[i] ^ k[i + 4];
+ }
+ /* compute KA */
+ camelliaF2(ka, SIGMA, 0);
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] ^= k[i];
+ }
+ camelliaF2(ka, SIGMA, 4);
+
+ if (_keyIs128)
+ {
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldq(15, k, 0, subkey, 4);
+ roldq(30, k, 0, subkey, 12);
+ roldq(15, k, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ roldq(17, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 24);
+ roldq(17, k, 0, subkey, 32);
+ /* KA dependant keys */
+ subkey[0] = ka[0];
+ subkey[1] = ka[1];
+ subkey[2] = ka[2];
+ subkey[3] = ka[3];
+ roldq(15, ka, 0, subkey, 8);
+ roldq(15, ka, 0, ke, 0);
+ roldq(15, ka, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ roldq(15, ka, 0, subkey, 20);
+ roldqo32(34, ka, 0, subkey, 28);
+ roldq(17, ka, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldq(15, k, 0, subkey, 28);
+ decroldq(30, k, 0, subkey, 20);
+ decroldq(15, k, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ decroldq(17, k, 0, ke, 0);
+ decroldq(17, k, 0, subkey, 8);
+ decroldq(17, k, 0, subkey, 0);
+ /* KA dependant keys */
+ subkey[34] = ka[0];
+ subkey[35] = ka[1];
+ subkey[32] = ka[2];
+ subkey[33] = ka[3];
+ decroldq(15, ka, 0, subkey, 24);
+ decroldq(15, ka, 0, ke, 4);
+ decroldq(15, ka, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ decroldq(15, ka, 0, subkey, 12);
+ decroldqo32(34, ka, 0, subkey, 4);
+ roldq(17, ka, 0, kw, 0);
+ }
+ }
+ else
+ { // 192bit or 256bit
+ /* compute KB */
+ for (int i = 0; i < 4; i++)
+ {
+ kb[i] = ka[i] ^ k[i + 4];
+ }
+ camelliaF2(kb, SIGMA, 8);
+
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldqo32(45, k, 0, subkey, 16);
+ roldq(15, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 32);
+ roldqo32(34, k, 0, subkey, 44);
+ /* KR dependant keys */
+ roldq(15, k, 4, subkey, 4);
+ roldq(15, k, 4, ke, 0);
+ roldq(30, k, 4, subkey, 24);
+ roldqo32(34, k, 4, subkey, 36);
+ /* KA dependant keys */
+ roldq(15, ka, 0, subkey, 8);
+ roldq(30, ka, 0, subkey, 20);
+ /* 32bit rotation */
+ ke[8] = ka[1];
+ ke[9] = ka[2];
+ ke[10] = ka[3];
+ ke[11] = ka[0];
+ roldqo32(49, ka, 0, subkey, 40);
+
+ /* KB dependant keys */
+ subkey[0] = kb[0];
+ subkey[1] = kb[1];
+ subkey[2] = kb[2];
+ subkey[3] = kb[3];
+ roldq(30, kb, 0, subkey, 12);
+ roldq(30, kb, 0, subkey, 28);
+ roldqo32(51, kb, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldqo32(45, k, 0, subkey, 28);
+ decroldq(15, k, 0, ke, 4);
+ decroldq(17, k, 0, subkey, 12);
+ decroldqo32(34, k, 0, subkey, 0);
+ /* KR dependant keys */
+ decroldq(15, k, 4, subkey, 40);
+ decroldq(15, k, 4, ke, 8);
+ decroldq(30, k, 4, subkey, 20);
+ decroldqo32(34, k, 4, subkey, 8);
+ /* KA dependant keys */
+ decroldq(15, ka, 0, subkey, 36);
+ decroldq(30, ka, 0, subkey, 24);
+ /* 32bit rotation */
+ ke[2] = ka[1];
+ ke[3] = ka[2];
+ ke[0] = ka[3];
+ ke[1] = ka[0];
+ decroldqo32(49, ka, 0, subkey, 4);
+
+ /* KB dependant keys */
+ subkey[46] = kb[0];
+ subkey[47] = kb[1];
+ subkey[44] = kb[2];
+ subkey[45] = kb[3];
+ decroldq(30, kb, 0, subkey, 32);
+ decroldq(30, kb, 0, subkey, 16);
+ roldqo32(51, kb, 0, kw, 0);
+ }
+ }
+ }
+
+ private int processBlock128(byte[] input, int inOff, byte[] output, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2uint(input, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ uint2bytes(state[2], output, outOff);
+ uint2bytes(state[3], output, outOff + 4);
+ uint2bytes(state[0], output, outOff + 8);
+ uint2bytes(state[1], output, outOff + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ private int processBlock192or256(byte[] input, int inOff, byte[] output, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2uint(input, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+ camelliaFLs(state, ke, 8);
+ camelliaF2(state, subkey, 36);
+ camelliaF2(state, subkey, 40);
+ camelliaF2(state, subkey, 44);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ uint2bytes(state[2], output, outOff);
+ uint2bytes(state[3], output, outOff + 4);
+ uint2bytes(state[0], output, outOff + 8);
+ uint2bytes(state[1], output, outOff + 12);
+ return BLOCK_SIZE;
+ }
+
+ public CamelliaEngine()
+ {
+ }
+
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("only simple KeyParameter expected.");
+
+ setKey(forEncryption, ((KeyParameter)parameters).GetKey());
+
+ initialised = true;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Camellia"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (!initialised)
+ throw new InvalidOperationException("Camellia engine not initialised");
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ if (_keyIs128)
+ {
+ return processBlock128(input, inOff, output, outOff);
+ }
+ else
+ {
+ return processBlock192or256(input, inOff, output, outOff);
+ }
+ }
+
+ public void Reset()
+ {
+ // nothing
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/CamelliaLightEngine.cs b/Crypto/src/crypto/engines/CamelliaLightEngine.cs
new file mode 100644
index 000000000..a301eb55e
--- /dev/null
+++ b/Crypto/src/crypto/engines/CamelliaLightEngine.cs
@@ -0,0 +1,581 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * Camellia - based on RFC 3713, smaller implementation, about half the size of CamelliaEngine.
+ */
+ public class CamelliaLightEngine
+ : IBlockCipher
+ {
+ private const int BLOCK_SIZE = 16;
+// private const int MASK8 = 0xff;
+ private bool initialised;
+ private bool _keyis128;
+
+ private uint[] subkey = new uint[24 * 4];
+ private uint[] kw = new uint[4 * 2]; // for whitening
+ private uint[] ke = new uint[6 * 2]; // for FL and FL^(-1)
+ private uint[] state = new uint[4]; // for encryption and decryption
+
+ private static readonly uint[] SIGMA = {
+ 0xa09e667f, 0x3bcc908b,
+ 0xb67ae858, 0x4caa73b2,
+ 0xc6ef372f, 0xe94f82be,
+ 0x54ff53a5, 0xf1d36f1c,
+ 0x10e527fa, 0xde682d1d,
+ 0xb05688c2, 0xb3e6c1fd
+ };
+
+ /*
+ *
+ * S-box data
+ *
+ */
+ private static readonly byte[] SBOX1 = {
+ (byte)112, (byte)130, (byte)44, (byte)236,
+ (byte)179, (byte)39, (byte)192, (byte)229,
+ (byte)228, (byte)133, (byte)87, (byte)53,
+ (byte)234, (byte)12, (byte)174, (byte)65,
+ (byte)35, (byte)239, (byte)107, (byte)147,
+ (byte)69, (byte)25, (byte)165, (byte)33,
+ (byte)237, (byte)14, (byte)79, (byte)78,
+ (byte)29, (byte)101, (byte)146, (byte)189,
+ (byte)134, (byte)184, (byte)175, (byte)143,
+ (byte)124, (byte)235, (byte)31, (byte)206,
+ (byte)62, (byte)48, (byte)220, (byte)95,
+ (byte)94, (byte)197, (byte)11, (byte)26,
+ (byte)166, (byte)225, (byte)57, (byte)202,
+ (byte)213, (byte)71, (byte)93, (byte)61,
+ (byte)217, (byte)1, (byte)90, (byte)214,
+ (byte)81, (byte)86, (byte)108, (byte)77,
+ (byte)139, (byte)13, (byte)154, (byte)102,
+ (byte)251, (byte)204, (byte)176, (byte)45,
+ (byte)116, (byte)18, (byte)43, (byte)32,
+ (byte)240, (byte)177, (byte)132, (byte)153,
+ (byte)223, (byte)76, (byte)203, (byte)194,
+ (byte)52, (byte)126, (byte)118, (byte)5,
+ (byte)109, (byte)183, (byte)169, (byte)49,
+ (byte)209, (byte)23, (byte)4, (byte)215,
+ (byte)20, (byte)88, (byte)58, (byte)97,
+ (byte)222, (byte)27, (byte)17, (byte)28,
+ (byte)50, (byte)15, (byte)156, (byte)22,
+ (byte)83, (byte)24, (byte)242, (byte)34,
+ (byte)254, (byte)68, (byte)207, (byte)178,
+ (byte)195, (byte)181, (byte)122, (byte)145,
+ (byte)36, (byte)8, (byte)232, (byte)168,
+ (byte)96, (byte)252, (byte)105, (byte)80,
+ (byte)170, (byte)208, (byte)160, (byte)125,
+ (byte)161, (byte)137, (byte)98, (byte)151,
+ (byte)84, (byte)91, (byte)30, (byte)149,
+ (byte)224, (byte)255, (byte)100, (byte)210,
+ (byte)16, (byte)196, (byte)0, (byte)72,
+ (byte)163, (byte)247, (byte)117, (byte)219,
+ (byte)138, (byte)3, (byte)230, (byte)218,
+ (byte)9, (byte)63, (byte)221, (byte)148,
+ (byte)135, (byte)92, (byte)131, (byte)2,
+ (byte)205, (byte)74, (byte)144, (byte)51,
+ (byte)115, (byte)103, (byte)246, (byte)243,
+ (byte)157, (byte)127, (byte)191, (byte)226,
+ (byte)82, (byte)155, (byte)216, (byte)38,
+ (byte)200, (byte)55, (byte)198, (byte)59,
+ (byte)129, (byte)150, (byte)111, (byte)75,
+ (byte)19, (byte)190, (byte)99, (byte)46,
+ (byte)233, (byte)121, (byte)167, (byte)140,
+ (byte)159, (byte)110, (byte)188, (byte)142,
+ (byte)41, (byte)245, (byte)249, (byte)182,
+ (byte)47, (byte)253, (byte)180, (byte)89,
+ (byte)120, (byte)152, (byte)6, (byte)106,
+ (byte)231, (byte)70, (byte)113, (byte)186,
+ (byte)212, (byte)37, (byte)171, (byte)66,
+ (byte)136, (byte)162, (byte)141, (byte)250,
+ (byte)114, (byte)7, (byte)185, (byte)85,
+ (byte)248, (byte)238, (byte)172, (byte)10,
+ (byte)54, (byte)73, (byte)42, (byte)104,
+ (byte)60, (byte)56, (byte)241, (byte)164,
+ (byte)64, (byte)40, (byte)211, (byte)123,
+ (byte)187, (byte)201, (byte)67, (byte)193,
+ (byte)21, (byte)227, (byte)173, (byte)244,
+ (byte)119, (byte)199, (byte)128, (byte)158
+ };
+
+ private static uint rightRotate(uint x, int s)
+ {
+ return ((x >> s) + (x << (32 - s)));
+ }
+
+ private static uint leftRotate(uint x, int s)
+ {
+ return (x << s) + (x >> (32 - s));
+ }
+
+ private static void roldq(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >> (32 - rot));
+ ko[1 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >> (32 - rot));
+ ko[2 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >> (32 - rot));
+ ko[3 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >> (32 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldq(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >> (32 - rot));
+ ko[3 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >> (32 - rot));
+ ko[0 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >> (32 - rot));
+ ko[1 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >> (32 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private static void roldqo32(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >> (64 - rot));
+ ko[1 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >> (64 - rot));
+ ko[2 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >> (64 - rot));
+ ko[3 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >> (64 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldqo32(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >> (64 - rot));
+ ko[3 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >> (64 - rot));
+ ko[0 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >> (64 - rot));
+ ko[1 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >> (64 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private static uint bytes2uint(byte[] src, int offset)
+ {
+ uint word = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ word = (word << 8) + (uint)src[i + offset];
+ }
+ return word;
+ }
+
+ private static void uint2bytes(uint word, byte[] dst, int offset)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ dst[(3 - i) + offset] = (byte)word;
+ word >>= 8;
+ }
+ }
+
+ private byte lRot8(byte v, int rot)
+ {
+ return (byte)(((uint)v << rot) | ((uint)v >> (8 - rot)));
+ }
+
+ private uint sbox2(int x)
+ {
+ return (uint)lRot8(SBOX1[x], 1);
+ }
+
+ private uint sbox3(int x)
+ {
+ return (uint)lRot8(SBOX1[x], 7);
+ }
+
+ private uint sbox4(int x)
+ {
+ return (uint)SBOX1[lRot8((byte)x, 1)];
+ }
+
+ private void camelliaF2(uint[] s, uint[] skey, int keyoff)
+ {
+ uint t1, t2, u, v;
+
+ t1 = s[0] ^ skey[0 + keyoff];
+ u = sbox4((byte)t1);
+ u |= (sbox3((byte)(t1 >> 8)) << 8);
+ u |= (sbox2((byte)(t1 >> 16)) << 16);
+ u |= ((uint)(SBOX1[(byte)(t1 >> 24)]) << 24);
+
+ t2 = s[1] ^ skey[1 + keyoff];
+ v = (uint)SBOX1[(byte)t2];
+ v |= (sbox4((byte)(t2 >> 8)) << 8);
+ v |= (sbox3((byte)(t2 >> 16)) << 16);
+ v |= (sbox2((byte)(t2 >> 24)) << 24);
+
+ v = leftRotate(v, 8);
+ u ^= v;
+ v = leftRotate(v, 8) ^ u;
+ u = rightRotate(u, 8) ^ v;
+ s[2] ^= leftRotate(v, 16) ^ u;
+ s[3] ^= leftRotate(u, 8);
+
+ t1 = s[2] ^ skey[2 + keyoff];
+ u = sbox4((byte)t1);
+ u |= sbox3((byte)(t1 >> 8)) << 8;
+ u |= sbox2((byte)(t1 >> 16)) << 16;
+ u |= ((uint)SBOX1[(byte)(t1 >> 24)]) << 24;
+
+ t2 = s[3] ^ skey[3 + keyoff];
+ v = (uint)SBOX1[(byte)t2];
+ v |= sbox4((byte)(t2 >> 8)) << 8;
+ v |= sbox3((byte)(t2 >> 16)) << 16;
+ v |= sbox2((byte)(t2 >> 24)) << 24;
+
+ v = leftRotate(v, 8);
+ u ^= v;
+ v = leftRotate(v, 8) ^ u;
+ u = rightRotate(u, 8) ^ v;
+ s[0] ^= leftRotate(v, 16) ^ u;
+ s[1] ^= leftRotate(u, 8);
+ }
+
+ private void camelliaFLs(uint[] s, uint[] fkey, int keyoff)
+ {
+ s[1] ^= leftRotate(s[0] & fkey[0 + keyoff], 1);
+ s[0] ^= fkey[1 + keyoff] | s[1];
+
+ s[2] ^= fkey[3 + keyoff] | s[3];
+ s[3] ^= leftRotate(fkey[2 + keyoff] & s[2], 1);
+ }
+
+ private void setKey(bool forEncryption, byte[] key)
+ {
+ uint[] k = new uint[8];
+ uint[] ka = new uint[4];
+ uint[] kb = new uint[4];
+ uint[] t = new uint[4];
+
+ switch (key.Length)
+ {
+ case 16:
+ _keyis128 = true;
+ k[0] = bytes2uint(key, 0);
+ k[1] = bytes2uint(key, 4);
+ k[2] = bytes2uint(key, 8);
+ k[3] = bytes2uint(key, 12);
+ k[4] = k[5] = k[6] = k[7] = 0;
+ break;
+ case 24:
+ k[0] = bytes2uint(key, 0);
+ k[1] = bytes2uint(key, 4);
+ k[2] = bytes2uint(key, 8);
+ k[3] = bytes2uint(key, 12);
+ k[4] = bytes2uint(key, 16);
+ k[5] = bytes2uint(key, 20);
+ k[6] = ~k[4];
+ k[7] = ~k[5];
+ _keyis128 = false;
+ break;
+ case 32:
+ k[0] = bytes2uint(key, 0);
+ k[1] = bytes2uint(key, 4);
+ k[2] = bytes2uint(key, 8);
+ k[3] = bytes2uint(key, 12);
+ k[4] = bytes2uint(key, 16);
+ k[5] = bytes2uint(key, 20);
+ k[6] = bytes2uint(key, 24);
+ k[7] = bytes2uint(key, 28);
+ _keyis128 = false;
+ break;
+ default:
+ throw new ArgumentException("key sizes are only 16/24/32 bytes.");
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] = k[i] ^ k[i + 4];
+ }
+ /* compute KA */
+ camelliaF2(ka, SIGMA, 0);
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] ^= k[i];
+ }
+ camelliaF2(ka, SIGMA, 4);
+
+ if (_keyis128)
+ {
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldq(15, k, 0, subkey, 4);
+ roldq(30, k, 0, subkey, 12);
+ roldq(15, k, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ roldq(17, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 24);
+ roldq(17, k, 0, subkey, 32);
+ /* KA dependant keys */
+ subkey[0] = ka[0];
+ subkey[1] = ka[1];
+ subkey[2] = ka[2];
+ subkey[3] = ka[3];
+ roldq(15, ka, 0, subkey, 8);
+ roldq(15, ka, 0, ke, 0);
+ roldq(15, ka, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ roldq(15, ka, 0, subkey, 20);
+ roldqo32(34, ka, 0, subkey, 28);
+ roldq(17, ka, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldq(15, k, 0, subkey, 28);
+ decroldq(30, k, 0, subkey, 20);
+ decroldq(15, k, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ decroldq(17, k, 0, ke, 0);
+ decroldq(17, k, 0, subkey, 8);
+ decroldq(17, k, 0, subkey, 0);
+ /* KA dependant keys */
+ subkey[34] = ka[0];
+ subkey[35] = ka[1];
+ subkey[32] = ka[2];
+ subkey[33] = ka[3];
+ decroldq(15, ka, 0, subkey, 24);
+ decroldq(15, ka, 0, ke, 4);
+ decroldq(15, ka, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ decroldq(15, ka, 0, subkey, 12);
+ decroldqo32(34, ka, 0, subkey, 4);
+ roldq(17, ka, 0, kw, 0);
+ }
+ }
+ else
+ { // 192bit or 256bit
+ /* compute KB */
+ for (int i = 0; i < 4; i++)
+ {
+ kb[i] = ka[i] ^ k[i + 4];
+ }
+ camelliaF2(kb, SIGMA, 8);
+
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldqo32(45, k, 0, subkey, 16);
+ roldq(15, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 32);
+ roldqo32(34, k, 0, subkey, 44);
+ /* KR dependant keys */
+ roldq(15, k, 4, subkey, 4);
+ roldq(15, k, 4, ke, 0);
+ roldq(30, k, 4, subkey, 24);
+ roldqo32(34, k, 4, subkey, 36);
+ /* KA dependant keys */
+ roldq(15, ka, 0, subkey, 8);
+ roldq(30, ka, 0, subkey, 20);
+ /* 32bit rotation */
+ ke[8] = ka[1];
+ ke[9] = ka[2];
+ ke[10] = ka[3];
+ ke[11] = ka[0];
+ roldqo32(49, ka, 0, subkey, 40);
+
+ /* KB dependant keys */
+ subkey[0] = kb[0];
+ subkey[1] = kb[1];
+ subkey[2] = kb[2];
+ subkey[3] = kb[3];
+ roldq(30, kb, 0, subkey, 12);
+ roldq(30, kb, 0, subkey, 28);
+ roldqo32(51, kb, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldqo32(45, k, 0, subkey, 28);
+ decroldq(15, k, 0, ke, 4);
+ decroldq(17, k, 0, subkey, 12);
+ decroldqo32(34, k, 0, subkey, 0);
+ /* KR dependant keys */
+ decroldq(15, k, 4, subkey, 40);
+ decroldq(15, k, 4, ke, 8);
+ decroldq(30, k, 4, subkey, 20);
+ decroldqo32(34, k, 4, subkey, 8);
+ /* KA dependant keys */
+ decroldq(15, ka, 0, subkey, 36);
+ decroldq(30, ka, 0, subkey, 24);
+ /* 32bit rotation */
+ ke[2] = ka[1];
+ ke[3] = ka[2];
+ ke[0] = ka[3];
+ ke[1] = ka[0];
+ decroldqo32(49, ka, 0, subkey, 4);
+
+ /* KB dependant keys */
+ subkey[46] = kb[0];
+ subkey[47] = kb[1];
+ subkey[44] = kb[2];
+ subkey[45] = kb[3];
+ decroldq(30, kb, 0, subkey, 32);
+ decroldq(30, kb, 0, subkey, 16);
+ roldqo32(51, kb, 0, kw, 0);
+ }
+ }
+ }
+
+ private int processBlock128(byte[] input, int inOff, byte[] output, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2uint(input, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ uint2bytes(state[2], output, outOff);
+ uint2bytes(state[3], output, outOff + 4);
+ uint2bytes(state[0], output, outOff + 8);
+ uint2bytes(state[1], output, outOff + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ private int processBlock192or256(byte[] input, int inOff, byte[] output, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2uint(input, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+ camelliaFLs(state, ke, 8);
+ camelliaF2(state, subkey, 36);
+ camelliaF2(state, subkey, 40);
+ camelliaF2(state, subkey, 44);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ uint2bytes(state[2], output, outOff);
+ uint2bytes(state[3], output, outOff + 4);
+ uint2bytes(state[0], output, outOff + 8);
+ uint2bytes(state[1], output, outOff + 12);
+ return BLOCK_SIZE;
+ }
+
+ public CamelliaLightEngine()
+ {
+ initialised = false;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Camellia"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("only simple KeyParameter expected.");
+
+ setKey(forEncryption, ((KeyParameter)parameters).GetKey());
+
+ initialised = true;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (!initialised)
+ throw new InvalidOperationException("Camellia engine not initialised");
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ if (_keyis128)
+ {
+ return processBlock128(input, inOff, output, outOff);
+ }
+ else
+ {
+ return processBlock192or256(input, inOff, output, outOff);
+ }
+ }
+
+ public void Reset()
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/CamelliaWrapEngine.cs b/Crypto/src/crypto/engines/CamelliaWrapEngine.cs
new file mode 100644
index 000000000..49dc833e6
--- /dev/null
+++ b/Crypto/src/crypto/engines/CamelliaWrapEngine.cs
@@ -0,0 +1,16 @@
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /// <remarks>
+ /// An implementation of the Camellia key wrapper based on RFC 3657/RFC 3394.
+ /// <p/>
+ /// For further details see: <a href="http://www.ietf.org/rfc/rfc3657.txt">http://www.ietf.org/rfc/rfc3657.txt</a>.
+ /// </remarks>
+ public class CamelliaWrapEngine
+ : Rfc3394WrapEngine
+ {
+ public CamelliaWrapEngine()
+ : base(new CamelliaEngine())
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/Cast5Engine.cs b/Crypto/src/crypto/engines/Cast5Engine.cs
new file mode 100644
index 000000000..4c3f84a55
--- /dev/null
+++ b/Crypto/src/crypto/engines/Cast5Engine.cs
@@ -0,0 +1,802 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * A class that provides CAST key encryption operations,
+ * such as encoding data and generating keys.
+ *
+ * All the algorithms herein are from the Internet RFC's
+ *
+ * RFC2144 - Cast5 (64bit block, 40-128bit key)
+ * RFC2612 - CAST6 (128bit block, 128-256bit key)
+ *
+ * and implement a simplified cryptography interface.
+ */
+ public class Cast5Engine
+ : IBlockCipher
+ {
+ internal static readonly uint[] S1 =
+ {
+ 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949,
+ 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
+ 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
+ 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0,
+ 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7,
+ 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
+ 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d,
+ 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50,
+ 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
+ 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3,
+ 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167,
+ 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
+ 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779,
+ 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2,
+ 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
+ 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d,
+ 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5,
+ 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324,
+ 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c,
+ 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,
+ 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d,
+ 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96,
+ 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,
+ 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
+ 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd,
+ 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,
+ 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
+ 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872,
+ 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,
+ 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
+ 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9,
+ 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf
+ },
+ S2 =
+ {
+ 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651,
+ 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3,
+ 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
+ 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806,
+ 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b,
+ 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
+ 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b,
+ 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c,
+ 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
+ 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb,
+ 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd,
+ 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
+ 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b,
+ 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304,
+ 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
+ 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf,
+ 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c,
+ 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
+ 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f,
+ 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6,
+ 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
+ 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58,
+ 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906,
+ 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
+ 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6,
+ 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4,
+ 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
+ 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f,
+ 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249,
+ 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
+ 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9,
+ 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1
+ },
+ S3 =
+ {
+ 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90,
+ 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5,
+ 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
+ 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240,
+ 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5,
+ 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
+ 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71,
+ 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04,
+ 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
+ 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15,
+ 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2,
+ 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
+ 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148,
+ 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc,
+ 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
+ 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e,
+ 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51,
+ 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
+ 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a,
+ 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b,
+ 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
+ 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5,
+ 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45,
+ 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
+ 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc,
+ 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0,
+ 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
+ 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2,
+ 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49,
+ 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
+ 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a,
+ 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783
+ },
+ S4 =
+ {
+ 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1,
+ 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf,
+ 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
+ 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121,
+ 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25,
+ 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
+ 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb,
+ 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5,
+ 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
+ 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6,
+ 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23,
+ 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
+ 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6,
+ 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119,
+ 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
+ 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a,
+ 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79,
+ 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
+ 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26,
+ 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab,
+ 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
+ 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417,
+ 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2,
+ 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
+ 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a,
+ 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919,
+ 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
+ 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876,
+ 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab,
+ 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
+ 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282,
+ 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2
+ },
+ S5 =
+ {
+ 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f,
+ 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a,
+ 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff,
+ 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02,
+ 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a,
+ 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7,
+ 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9,
+ 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981,
+ 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774,
+ 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655,
+ 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2,
+ 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910,
+ 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1,
+ 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da,
+ 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049,
+ 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f,
+ 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba,
+ 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be,
+ 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3,
+ 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840,
+ 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4,
+ 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2,
+ 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7,
+ 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5,
+ 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e,
+ 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e,
+ 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801,
+ 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad,
+ 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0,
+ 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20,
+ 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8,
+ 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4
+ },
+ S6 =
+ {
+ 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac,
+ 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138,
+ 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367,
+ 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98,
+ 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072,
+ 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3,
+ 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd,
+ 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8,
+ 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9,
+ 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54,
+ 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387,
+ 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc,
+ 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf,
+ 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf,
+ 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f,
+ 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289,
+ 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950,
+ 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f,
+ 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b,
+ 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be,
+ 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13,
+ 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976,
+ 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0,
+ 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891,
+ 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da,
+ 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc,
+ 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084,
+ 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25,
+ 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121,
+ 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5,
+ 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd,
+ 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f
+ },
+ S7 =
+ {
+ 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f,
+ 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de,
+ 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43,
+ 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19,
+ 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2,
+ 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516,
+ 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88,
+ 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816,
+ 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756,
+ 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a,
+ 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264,
+ 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688,
+ 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28,
+ 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3,
+ 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7,
+ 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06,
+ 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033,
+ 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a,
+ 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566,
+ 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509,
+ 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962,
+ 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e,
+ 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c,
+ 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c,
+ 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285,
+ 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301,
+ 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be,
+ 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767,
+ 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647,
+ 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914,
+ 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c,
+ 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3
+ },
+ S8 =
+ {
+ 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5,
+ 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc,
+ 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd,
+ 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d,
+ 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2,
+ 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862,
+ 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc,
+ 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c,
+ 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e,
+ 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039,
+ 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8,
+ 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42,
+ 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5,
+ 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472,
+ 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225,
+ 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c,
+ 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb,
+ 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054,
+ 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70,
+ 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc,
+ 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c,
+ 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3,
+ 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4,
+ 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101,
+ 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f,
+ 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e,
+ 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a,
+ 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c,
+ 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384,
+ 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c,
+ 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82,
+ 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e
+ };
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ internal static readonly int MAX_ROUNDS = 16;
+ internal static readonly int RED_ROUNDS = 12;
+
+ private const int BLOCK_SIZE = 8; // bytes = 64 bits
+
+ private int[] _Kr = new int[17]; // the rotating round key
+ private uint[] _Km = new uint[17]; // the masking round key
+
+ private bool _encrypting;
+
+ private byte[] _workingKey;
+ private int _rounds = MAX_ROUNDS;
+
+ public Cast5Engine()
+ {
+ }
+
+ /**
+ * initialise a CAST cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("Invalid parameter passed to "+ AlgorithmName +" init - " + parameters.GetType().ToString());
+
+ _encrypting = forEncryption;
+ _workingKey = ((KeyParameter)parameters).GetKey();
+ SetKey(_workingKey);
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return "CAST5"; }
+ }
+
+ public virtual bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public virtual int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ int blockSize = GetBlockSize();
+ if (_workingKey == null)
+ throw new InvalidOperationException(AlgorithmName + " not initialised");
+ if ((inOff + blockSize) > input.Length)
+ throw new DataLengthException("Input buffer too short");
+ if ((outOff + blockSize) > output.Length)
+ throw new DataLengthException("Output buffer too short");
+
+ if (_encrypting)
+ {
+ return EncryptBlock(input, inOff, output, outOff);
+ }
+ else
+ {
+ return DecryptBlock(input, inOff, output, outOff);
+ }
+ }
+
+ public virtual void Reset()
+ {
+ }
+
+ public virtual int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ /*
+ * Creates the subkeys using the same nomenclature
+ * as described in RFC2144.
+ *
+ * See section 2.4
+ */
+ internal virtual void SetKey(byte[] key)
+ {
+ /*
+ * Determine the key size here, if required
+ *
+ * if keysize <= 80bits, use 12 rounds instead of 16
+ * if keysize < 128bits, pad with 0
+ *
+ * Typical key sizes => 40, 64, 80, 128
+ */
+
+ if (key.Length < 11)
+ {
+ _rounds = RED_ROUNDS;
+ }
+
+ int [] z = new int[16];
+ int [] x = new int[16];
+
+ uint z03, z47, z8B, zCF;
+ uint x03, x47, x8B, xCF;
+
+ /* copy the key into x */
+ for (int i=0; i< key.Length; i++)
+ {
+ x[i] = (int)(key[i] & 0xff);
+ }
+
+ /*
+ * This will look different because the selection of
+ * bytes from the input key I've already chosen the
+ * correct int.
+ */
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Km[ 1]= S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]];
+ _Km[ 2]= S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]];
+ _Km[ 3]= S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]];
+ _Km[ 4]= S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]];
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Km[ 5]= S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]];
+ _Km[ 6]= S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]];
+ _Km[ 7]= S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]];
+ _Km[ 8]= S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]];
+
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Km[ 9]= S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]];
+ _Km[10]= S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]];
+ _Km[11]= S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]];
+ _Km[12]= S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]];
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Km[13]= S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]];
+ _Km[14]= S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]];
+ _Km[15]= S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]];
+ _Km[16]= S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]];
+
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Kr[ 1]=(int)((S5[z[0x8]]^S6[z[0x9]]^S7[z[0x7]]^S8[z[0x6]] ^ S5[z[0x2]])&0x1f);
+ _Kr[ 2]=(int)((S5[z[0xA]]^S6[z[0xB]]^S7[z[0x5]]^S8[z[0x4]] ^ S6[z[0x6]])&0x1f);
+ _Kr[ 3]=(int)((S5[z[0xC]]^S6[z[0xD]]^S7[z[0x3]]^S8[z[0x2]] ^ S7[z[0x9]])&0x1f);
+ _Kr[ 4]=(int)((S5[z[0xE]]^S6[z[0xF]]^S7[z[0x1]]^S8[z[0x0]] ^ S8[z[0xC]])&0x1f);
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Kr[ 5]=(int)((S5[x[0x3]]^S6[x[0x2]]^S7[x[0xC]]^S8[x[0xD]]^S5[x[0x8]])&0x1f);
+ _Kr[ 6]=(int)((S5[x[0x1]]^S6[x[0x0]]^S7[x[0xE]]^S8[x[0xF]]^S6[x[0xD]])&0x1f);
+ _Kr[ 7]=(int)((S5[x[0x7]]^S6[x[0x6]]^S7[x[0x8]]^S8[x[0x9]]^S7[x[0x3]])&0x1f);
+ _Kr[ 8]=(int)((S5[x[0x5]]^S6[x[0x4]]^S7[x[0xA]]^S8[x[0xB]]^S8[x[0x7]])&0x1f);
+
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Kr[ 9]=(int)((S5[z[0x3]]^S6[z[0x2]]^S7[z[0xC]]^S8[z[0xD]]^S5[z[0x9]])&0x1f);
+ _Kr[10]=(int)((S5[z[0x1]]^S6[z[0x0]]^S7[z[0xE]]^S8[z[0xF]]^S6[z[0xc]])&0x1f);
+ _Kr[11]=(int)((S5[z[0x7]]^S6[z[0x6]]^S7[z[0x8]]^S8[z[0x9]]^S7[z[0x2]])&0x1f);
+ _Kr[12]=(int)((S5[z[0x5]]^S6[z[0x4]]^S7[z[0xA]]^S8[z[0xB]]^S8[z[0x6]])&0x1f);
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Kr[13]=(int)((S5[x[0x8]]^S6[x[0x9]]^S7[x[0x7]]^S8[x[0x6]]^S5[x[0x3]])&0x1f);
+ _Kr[14]=(int)((S5[x[0xA]]^S6[x[0xB]]^S7[x[0x5]]^S8[x[0x4]]^S6[x[0x7]])&0x1f);
+ _Kr[15]=(int)((S5[x[0xC]]^S6[x[0xD]]^S7[x[0x3]]^S8[x[0x2]]^S7[x[0x8]])&0x1f);
+ _Kr[16]=(int)((S5[x[0xE]]^S6[x[0xF]]^S7[x[0x1]]^S8[x[0x0]]^S8[x[0xD]])&0x1f);
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ internal virtual int EncryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ // process the input block
+ // batch the units up into a 32 bit chunk and go for it
+ // the array is in bytes, the increment is 8x8 bits = 64
+
+ uint L0 = Pack.BE_To_UInt32(src, srcIndex);
+ uint R0 = Pack.BE_To_UInt32(src, srcIndex + 4);
+
+ uint[] result = new uint[2];
+ CAST_Encipher(L0, R0, result);
+
+ // now stuff them into the destination block
+ Pack.UInt32_To_BE(result[0], dst, dstIndex);
+ Pack.UInt32_To_BE(result[1], dst, dstIndex + 4);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ internal virtual int DecryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ // process the input block
+ // batch the units up into a 32 bit chunk and go for it
+ // the array is in bytes, the increment is 8x8 bits = 64
+ uint L16 = Pack.BE_To_UInt32(src, srcIndex);
+ uint R16 = Pack.BE_To_UInt32(src, srcIndex + 4);
+
+ uint[] result = new uint[2];
+ CAST_Decipher(L16, R16, result);
+
+ // now stuff them into the destination block
+ Pack.UInt32_To_BE(result[0], dst, dstIndex);
+ Pack.UInt32_To_BE(result[1], dst, dstIndex + 4);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * The first of the three processing functions for the
+ * encryption and decryption.
+ *
+ * @param D the input to be processed
+ * @param Kmi the mask to be used from Km[n]
+ * @param Kri the rotation value to be used
+ *
+ */
+ internal static uint F1(uint D, uint Kmi, int Kri)
+ {
+ uint I = Kmi + D;
+ I = I << Kri | (I >> (32-Kri));
+ return ((S1[(I>>24)&0xff]^S2[(I>>16)&0xff])-S3[(I>>8)&0xff])+S4[I&0xff];
+ }
+
+ /**
+ * The second of the three processing functions for the
+ * encryption and decryption.
+ *
+ * @param D the input to be processed
+ * @param Kmi the mask to be used from Km[n]
+ * @param Kri the rotation value to be used
+ *
+ */
+ internal static uint F2(uint D, uint Kmi, int Kri)
+ {
+ uint I = Kmi ^ D;
+ I = I << Kri | (I >> (32-Kri));
+ return ((S1[(I>>24)&0xff]-S2[(I>>16)&0xff])+S3[(I>>8)&0xff])^S4[I&0xff];
+ }
+
+ /**
+ * The third of the three processing functions for the
+ * encryption and decryption.
+ *
+ * @param D the input to be processed
+ * @param Kmi the mask to be used from Km[n]
+ * @param Kri the rotation value to be used
+ *
+ */
+ internal static uint F3(uint D, uint Kmi, int Kri)
+ {
+ uint I = Kmi - D;
+ I = I << Kri | (I >> (32-Kri));
+ return ((S1[(I>>24)&0xff]+S2[(I>>16)&0xff])^S3[(I>>8)&0xff])-S4[I&0xff];
+ }
+
+ /**
+ * Does the 16 rounds to encrypt the block.
+ *
+ * @param L0 the LH-32bits of the plaintext block
+ * @param R0 the RH-32bits of the plaintext block
+ */
+ internal void CAST_Encipher(uint L0, uint R0, uint[] result)
+ {
+ uint Lp = L0; // the previous value, equiv to L[i-1]
+ uint Rp = R0; // equivalent to R[i-1]
+
+ /*
+ * numbering consistent with paper to make
+ * checking and validating easier
+ */
+ uint Li = L0, Ri = R0;
+
+ for (int i = 1; i<=_rounds ; i++)
+ {
+ Lp = Li;
+ Rp = Ri;
+
+ Li = Rp;
+ switch (i)
+ {
+ case 1:
+ case 4:
+ case 7:
+ case 10:
+ case 13:
+ case 16:
+ Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
+ break;
+ case 2:
+ case 5:
+ case 8:
+ case 11:
+ case 14:
+ Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
+ break;
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
+ break;
+ }
+ }
+
+ result[0] = Ri;
+ result[1] = Li;
+
+ return;
+ }
+
+ internal void CAST_Decipher(uint L16, uint R16, uint[] result)
+ {
+ uint Lp = L16; // the previous value, equiv to L[i-1]
+ uint Rp = R16; // equivalent to R[i-1]
+
+ /*
+ * numbering consistent with paper to make
+ * checking and validating easier
+ */
+ uint Li = L16, Ri = R16;
+
+ for (int i = _rounds; i > 0; i--)
+ {
+ Lp = Li;
+ Rp = Ri;
+
+ Li = Rp;
+ switch (i)
+ {
+ case 1:
+ case 4:
+ case 7:
+ case 10:
+ case 13:
+ case 16:
+ Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
+ break;
+ case 2:
+ case 5:
+ case 8:
+ case 11:
+ case 14:
+ Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
+ break;
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
+ break;
+ }
+ }
+
+ result[0] = Ri;
+ result[1] = Li;
+
+ return;
+ }
+
+ internal static void Bits32ToInts(uint inData, int[] b, int offset)
+ {
+ b[offset + 3] = (int) (inData & 0xff);
+ b[offset + 2] = (int) ((inData >> 8) & 0xff);
+ b[offset + 1] = (int) ((inData >> 16) & 0xff);
+ b[offset] = (int) ((inData >> 24) & 0xff);
+ }
+
+ internal static uint IntsTo32bits(int[] b, int i)
+ {
+ return (uint)(((b[i] & 0xff) << 24) |
+ ((b[i+1] & 0xff) << 16) |
+ ((b[i+2] & 0xff) << 8) |
+ ((b[i+3] & 0xff)));
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/Cast6Engine.cs b/Crypto/src/crypto/engines/Cast6Engine.cs
new file mode 100644
index 000000000..c5c419b78
--- /dev/null
+++ b/Crypto/src/crypto/engines/Cast6Engine.cs
@@ -0,0 +1,279 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * A class that provides CAST6 key encryption operations,
+ * such as encoding data and generating keys.
+ *
+ * All the algorithms herein are from the Internet RFC
+ *
+ * RFC2612 - CAST6 (128bit block, 128-256bit key)
+ *
+ * and implement a simplified cryptography interface.
+ */
+ public sealed class Cast6Engine
+ : Cast5Engine
+ {
+ //====================================
+ // Useful constants
+ //====================================
+ private const int ROUNDS = 12;
+ private const int BLOCK_SIZE = 16; // bytes = 128 bits
+
+ /*
+ * Put the round and mask keys into an array.
+ * Kr0[i] => _Kr[i*4 + 0]
+ */
+ private int []_Kr = new int[ROUNDS*4]; // the rotating round key(s)
+ private uint []_Km = new uint[ROUNDS*4]; // the masking round key(s)
+
+ /*
+ * Key setup
+ */
+ private int []_Tr = new int[24 * 8];
+ private uint []_Tm = new uint[24 * 8];
+ private uint[] _workingKey = new uint[8];
+
+ public Cast6Engine()
+ {
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "CAST6"; }
+ }
+
+ public override void Reset()
+ {
+ }
+
+ public override int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+ /*
+ * Creates the subkeys using the same nomenclature
+ * as described in RFC2612.
+ *
+ * See section 2.4
+ */
+ internal override void SetKey(
+ byte[] key)
+ {
+ uint Cm = 0x5a827999;
+ uint Mm = 0x6ed9eba1;
+ int Cr = 19;
+ int Mr = 17;
+ /*
+ * Determine the key size here, if required
+ *
+ * if keysize < 256 bytes, pad with 0
+ *
+ * Typical key sizes => 128, 160, 192, 224, 256
+ */
+ for (int i=0; i< 24; i++)
+ {
+ for (int j=0; j< 8; j++)
+ {
+ _Tm[i*8 + j] = Cm;
+ Cm += Mm; //mod 2^32;
+ _Tr[i*8 + j] = Cr;
+ Cr = (Cr + Mr) & 0x1f; // mod 32
+ }
+ }
+
+ byte[] tmpKey = new byte[64];
+ key.CopyTo(tmpKey, 0);
+
+ // now create ABCDEFGH
+ for (int i = 0; i < 8; i++)
+ {
+ _workingKey[i] = Pack.BE_To_UInt32(tmpKey, i*4);
+ }
+
+ // Generate the key schedule
+ for (int i = 0; i < 12; i++)
+ {
+ // KAPPA <- W2i(KAPPA)
+ int i2 = i*2 *8;
+ _workingKey[6] ^= F1(_workingKey[7], _Tm[i2], _Tr[i2]);
+ _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]);
+ _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]);
+ _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]);
+ _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]);
+ _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]);
+ _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]);
+ _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]);
+ // KAPPA <- W2i+1(KAPPA)
+ i2 = (i*2 + 1)*8;
+ _workingKey[6] ^= F1(_workingKey[7], _Tm[i2], _Tr[i2]);
+ _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]);
+ _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]);
+ _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]);
+ _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]);
+ _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]);
+ _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]);
+ _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]);
+ // Kr_(i) <- KAPPA
+ _Kr[i*4] = (int)(_workingKey[0] & 0x1f);
+ _Kr[i*4 + 1] = (int)(_workingKey[2] & 0x1f);
+ _Kr[i*4 + 2] = (int)(_workingKey[4] & 0x1f);
+ _Kr[i*4 + 3] = (int)(_workingKey[6] & 0x1f);
+ // Km_(i) <- KAPPA
+ _Km[i*4] = _workingKey[7];
+ _Km[i*4 + 1] = _workingKey[5];
+ _Km[i*4 + 2] = _workingKey[3];
+ _Km[i*4 + 3] = _workingKey[1];
+ }
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ internal override int EncryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ // process the input block
+ // batch the units up into 4x32 bit chunks and go for it
+ uint A = Pack.BE_To_UInt32(src, srcIndex);
+ uint B = Pack.BE_To_UInt32(src, srcIndex + 4);
+ uint C = Pack.BE_To_UInt32(src, srcIndex + 8);
+ uint D = Pack.BE_To_UInt32(src, srcIndex + 12);
+ uint[] result = new uint[4];
+ CAST_Encipher(A, B, C, D, result);
+ // now stuff them into the destination block
+ Pack.UInt32_To_BE(result[0], dst, dstIndex);
+ Pack.UInt32_To_BE(result[1], dst, dstIndex + 4);
+ Pack.UInt32_To_BE(result[2], dst, dstIndex + 8);
+ Pack.UInt32_To_BE(result[3], dst, dstIndex + 12);
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ internal override int DecryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ // process the input block
+ // batch the units up into 4x32 bit chunks and go for it
+ uint A = Pack.BE_To_UInt32(src, srcIndex);
+ uint B = Pack.BE_To_UInt32(src, srcIndex + 4);
+ uint C = Pack.BE_To_UInt32(src, srcIndex + 8);
+ uint D = Pack.BE_To_UInt32(src, srcIndex + 12);
+ uint[] result = new uint[4];
+ CAST_Decipher(A, B, C, D, result);
+ // now stuff them into the destination block
+ Pack.UInt32_To_BE(result[0], dst, dstIndex);
+ Pack.UInt32_To_BE(result[1], dst, dstIndex + 4);
+ Pack.UInt32_To_BE(result[2], dst, dstIndex + 8);
+ Pack.UInt32_To_BE(result[3], dst, dstIndex + 12);
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Does the 12 quad rounds rounds to encrypt the block.
+ *
+ * @param A the 00-31 bits of the plaintext block
+ * @param B the 32-63 bits of the plaintext block
+ * @param C the 64-95 bits of the plaintext block
+ * @param D the 96-127 bits of the plaintext block
+ * @param result the resulting ciphertext
+ */
+ private void CAST_Encipher(
+ uint A,
+ uint B,
+ uint C,
+ uint D,
+ uint[] result)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ int x = i*4;
+ // BETA <- Qi(BETA)
+ C ^= F1(D, _Km[x], _Kr[x]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+ }
+ for (int i = 6; i < 12; i++)
+ {
+ int x = i*4;
+ // BETA <- QBARi(BETA)
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ C ^= F1(D, _Km[x], _Kr[x]);
+ }
+ result[0] = A;
+ result[1] = B;
+ result[2] = C;
+ result[3] = D;
+ }
+
+ /**
+ * Does the 12 quad rounds rounds to decrypt the block.
+ *
+ * @param A the 00-31 bits of the ciphertext block
+ * @param B the 32-63 bits of the ciphertext block
+ * @param C the 64-95 bits of the ciphertext block
+ * @param D the 96-127 bits of the ciphertext block
+ * @param result the resulting plaintext
+ */
+ private void CAST_Decipher(
+ uint A,
+ uint B,
+ uint C,
+ uint D,
+ uint[] result)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ int x = (11-i)*4;
+ // BETA <- Qi(BETA)
+ C ^= F1(D, _Km[x], _Kr[x]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+ }
+ for (int i=6; i<12; i++)
+ {
+ int x = (11-i)*4;
+ // BETA <- QBARi(BETA)
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ C ^= F1(D, _Km[x], _Kr[x]);
+ }
+ result[0] = A;
+ result[1] = B;
+ result[2] = C;
+ result[3] = D;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/DesEdeEngine.cs b/Crypto/src/crypto/engines/DesEdeEngine.cs
new file mode 100644
index 000000000..b319888e3
--- /dev/null
+++ b/Crypto/src/crypto/engines/DesEdeEngine.cs
@@ -0,0 +1,100 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /// <remarks>A class that provides a basic DESede (or Triple DES) engine.</remarks>
+ public class DesEdeEngine
+ : DesEngine
+ {
+ private int[] workingKey1, workingKey2, workingKey3;
+ private bool forEncryption;
+
+ /**
+ * initialise a DESede cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public override void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ {
+ throw new ArgumentException("invalid parameter passed to DESede init - " + parameters.GetType().ToString());
+ }
+
+ byte[] keyMaster = ((KeyParameter)parameters).GetKey();
+
+ this.forEncryption = forEncryption;
+
+ byte[] key1 = new byte[8];
+ Array.Copy(keyMaster, 0, key1, 0, key1.Length);
+ workingKey1 = GenerateWorkingKey(forEncryption, key1);
+
+ byte[] key2 = new byte[8];
+ Array.Copy(keyMaster, 8, key2, 0, key2.Length);
+ workingKey2 = GenerateWorkingKey(!forEncryption, key2);
+
+ if (keyMaster.Length == 24)
+ {
+ byte[] key3 = new byte[8];
+ Array.Copy(keyMaster, 16, key3, 0, key3.Length);
+ workingKey3 = GenerateWorkingKey(forEncryption, key3);
+ }
+ else // 16 byte key
+ {
+ workingKey3 = workingKey1;
+ }
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "DESede"; }
+ }
+
+ public override int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public override int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (workingKey1 == null)
+ throw new InvalidOperationException("DESede engine not initialised");
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ byte[] temp = new byte[BLOCK_SIZE];
+
+ if (forEncryption)
+ {
+ DesFunc(workingKey1, input, inOff, temp, 0);
+ DesFunc(workingKey2, temp, 0, temp, 0);
+ DesFunc(workingKey3, temp, 0, output, outOff);
+ }
+ else
+ {
+ DesFunc(workingKey3, input, inOff, temp, 0);
+ DesFunc(workingKey2, temp, 0, temp, 0);
+ DesFunc(workingKey1, temp, 0, output, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public override void Reset()
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/DesEdeWrapEngine.cs b/Crypto/src/crypto/engines/DesEdeWrapEngine.cs
new file mode 100644
index 000000000..fdc71687f
--- /dev/null
+++ b/Crypto/src/crypto/engines/DesEdeWrapEngine.cs
@@ -0,0 +1,322 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * Wrap keys according to
+ * <a href="http://www.ietf.org/internet-drafts/draft-ietf-smime-key-wrap-01.txt">
+ * draft-ietf-smime-key-wrap-01.txt</a>.
+ * <p>
+ * Note:
+ * <ul>
+ * <li>this is based on a draft, and as such is subject to change - don't use this class for anything requiring long term storage.</li>
+ * <li>if you are using this to wrap triple-des keys you need to set the
+ * parity bits on the key and, if it's a two-key triple-des key, pad it
+ * yourself.</li>
+ * </ul>
+ * </p>
+ */
+ public class DesEdeWrapEngine
+ : IWrapper
+ {
+ /** Field engine */
+ private CbcBlockCipher engine;
+ /** Field param */
+ private KeyParameter param;
+ /** Field paramPlusIV */
+ private ParametersWithIV paramPlusIV;
+ /** Field iv */
+ private byte[] iv;
+ /** Field forWrapping */
+ private bool forWrapping;
+ /** Field IV2 */
+ private static readonly byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2,
+ (byte) 0x2c, (byte) 0x79, (byte) 0xe8,
+ (byte) 0x21, (byte) 0x05 };
+
+ //
+ // checksum digest
+ //
+ private readonly IDigest sha1 = new Sha1Digest();
+ private readonly byte[] digest = new byte[20];
+
+ /**
+ * Method init
+ *
+ * @param forWrapping
+ * @param param
+ */
+ public void Init(
+ bool forWrapping,
+ ICipherParameters parameters)
+ {
+ this.forWrapping = forWrapping;
+ this.engine = new CbcBlockCipher(new DesEdeEngine());
+
+ SecureRandom sr;
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom pr = (ParametersWithRandom) parameters;
+ parameters = pr.Parameters;
+ sr = pr.Random;
+ }
+ else
+ {
+ sr = new SecureRandom();
+ }
+
+ if (parameters is KeyParameter)
+ {
+ this.param = (KeyParameter) parameters;
+ if (this.forWrapping)
+ {
+ // Hm, we have no IV but we want to wrap ?!?
+ // well, then we have to create our own IV.
+ this.iv = new byte[8];
+ sr.NextBytes(iv);
+
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+ }
+ }
+ else if (parameters is ParametersWithIV)
+ {
+ if (!forWrapping)
+ throw new ArgumentException("You should not supply an IV for unwrapping");
+
+ this.paramPlusIV = (ParametersWithIV) parameters;
+ this.iv = this.paramPlusIV.GetIV();
+ this.param = (KeyParameter) this.paramPlusIV.Parameters;
+
+ if (this.iv.Length != 8)
+ throw new ArgumentException("IV is not 8 octets", "parameters");
+ }
+ }
+
+ /**
+ * Method GetAlgorithmName
+ *
+ * @return
+ */
+ public string AlgorithmName
+ {
+ get { return "DESede"; }
+ }
+
+ /**
+ * Method wrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return
+ */
+ public byte[] Wrap(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (!forWrapping)
+ {
+ throw new InvalidOperationException("Not initialized for wrapping");
+ }
+
+ byte[] keyToBeWrapped = new byte[length];
+ Array.Copy(input, inOff, keyToBeWrapped, 0, length);
+
+ // Compute the CMS Key Checksum, (section 5.6.1), call this CKS.
+ byte[] CKS = CalculateCmsKeyChecksum(keyToBeWrapped);
+
+ // Let WKCKS = WK || CKS where || is concatenation.
+ byte[] WKCKS = new byte[keyToBeWrapped.Length + CKS.Length];
+ Array.Copy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.Length);
+ Array.Copy(CKS, 0, WKCKS, keyToBeWrapped.Length, CKS.Length);
+
+ // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
+ // initialization vector. Call the results TEMP1.
+
+ int blockSize = engine.GetBlockSize();
+
+ if (WKCKS.Length % blockSize != 0)
+ throw new InvalidOperationException("Not multiple of block length");
+
+ engine.Init(true, paramPlusIV);
+
+ byte [] TEMP1 = new byte[WKCKS.Length];
+
+ for (int currentBytePos = 0; currentBytePos != WKCKS.Length; currentBytePos += blockSize)
+ {
+ engine.ProcessBlock(WKCKS, currentBytePos, TEMP1, currentBytePos);
+ }
+
+ // Let TEMP2 = IV || TEMP1.
+ byte[] TEMP2 = new byte[this.iv.Length + TEMP1.Length];
+ Array.Copy(this.iv, 0, TEMP2, 0, this.iv.Length);
+ Array.Copy(TEMP1, 0, TEMP2, this.iv.Length, TEMP1.Length);
+
+ // Reverse the order of the octets in TEMP2 and call the result TEMP3.
+ byte[] TEMP3 = reverse(TEMP2);
+
+ // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
+ // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
+ // result. It is 40 octets long if a 168 bit key is being wrapped.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+ this.engine.Init(true, param2);
+
+ for (int currentBytePos = 0; currentBytePos != TEMP3.Length; currentBytePos += blockSize)
+ {
+ engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ return TEMP3;
+ }
+
+ /**
+ * Method unwrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return
+ * @throws InvalidCipherTextException
+ */
+ public byte[] Unwrap(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (forWrapping)
+ {
+ throw new InvalidOperationException("Not set for unwrapping");
+ }
+ if (input == null)
+ {
+ throw new InvalidCipherTextException("Null pointer as ciphertext");
+ }
+
+ int blockSize = engine.GetBlockSize();
+
+ if (length % blockSize != 0)
+ {
+ throw new InvalidCipherTextException("Ciphertext not multiple of " + blockSize);
+ }
+
+ /*
+ // Check if the length of the cipher text is reasonable given the key
+ // type. It must be 40 bytes for a 168 bit key and either 32, 40, or
+ // 48 bytes for a 128, 192, or 256 bit key. If the length is not supported
+ // or inconsistent with the algorithm for which the key is intended,
+ // return error.
+ //
+ // we do not accept 168 bit keys. it has to be 192 bit.
+ int lengthA = (estimatedKeyLengthInBit / 8) + 16;
+ int lengthB = estimatedKeyLengthInBit % 8;
+ if ((lengthA != keyToBeUnwrapped.Length) || (lengthB != 0)) {
+ throw new XMLSecurityException("empty");
+ }
+ */
+
+ // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK
+ // and an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+ this.engine.Init(false, param2);
+
+ byte [] TEMP3 = new byte[length];
+
+ for (int currentBytePos = 0; currentBytePos != TEMP3.Length; currentBytePos += blockSize)
+ {
+ engine.ProcessBlock(input, inOff + currentBytePos, TEMP3, currentBytePos);
+ }
+
+ // Reverse the order of the octets in TEMP3 and call the result TEMP2.
+ byte[] TEMP2 = reverse(TEMP3);
+
+ // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
+ this.iv = new byte[8];
+ byte[] TEMP1 = new byte[TEMP2.Length - 8];
+ Array.Copy(TEMP2, 0, this.iv, 0, 8);
+ Array.Copy(TEMP2, 8, TEMP1, 0, TEMP2.Length - 8);
+
+ // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV
+ // found in the previous step. Call the result WKCKS.
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+ this.engine.Init(false, this.paramPlusIV);
+
+ byte[] WKCKS = new byte[TEMP1.Length];
+
+ for (int currentBytePos = 0; currentBytePos != WKCKS.Length; currentBytePos += blockSize)
+ {
+ engine.ProcessBlock(TEMP1, currentBytePos, WKCKS, currentBytePos);
+ }
+
+ // Decompose WKCKS. CKS is the last 8 octets and WK, the wrapped key, are
+ // those octets before the CKS.
+ byte[] result = new byte[WKCKS.Length - 8];
+ byte[] CKStoBeVerified = new byte[8];
+ Array.Copy(WKCKS, 0, result, 0, WKCKS.Length - 8);
+ Array.Copy(WKCKS, WKCKS.Length - 8, CKStoBeVerified, 0, 8);
+
+ // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and compare
+ // with the CKS extracted in the above step. If they are not equal, return error.
+ if (!CheckCmsKeyChecksum(result, CKStoBeVerified)) {
+ throw new InvalidCipherTextException(
+ "Checksum inside ciphertext is corrupted");
+ }
+
+ // WK is the wrapped key, now extracted for use in data decryption.
+ return result;
+ }
+
+ /**
+ * Some key wrap algorithms make use of the Key Checksum defined
+ * in CMS [CMS-Algorithms]. This is used to provide an integrity
+ * check value for the key being wrapped. The algorithm is
+ *
+ * - Compute the 20 octet SHA-1 hash on the key being wrapped.
+ * - Use the first 8 octets of this hash as the checksum value.
+ *
+ * @param key
+ * @return
+ * @throws Exception
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private byte[] CalculateCmsKeyChecksum(
+ byte[] key)
+ {
+ sha1.BlockUpdate(key, 0, key.Length);
+ sha1.DoFinal(digest, 0);
+
+ byte[] result = new byte[8];
+ Array.Copy(digest, 0, result, 0, 8);
+ return result;
+ }
+
+ /**
+ * @param key
+ * @param checksum
+ * @return
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private bool CheckCmsKeyChecksum(
+ byte[] key,
+ byte[] checksum)
+ {
+ return Arrays.ConstantTimeAreEqual(CalculateCmsKeyChecksum(key), checksum);
+ }
+
+ private static byte[] reverse(byte[] bs)
+ {
+ byte[] result = new byte[bs.Length];
+ for (int i = 0; i < bs.Length; i++)
+ {
+ result[i] = bs[bs.Length - (i + 1)];
+ }
+ return result;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/DesEngine.cs b/Crypto/src/crypto/engines/DesEngine.cs
new file mode 100644
index 000000000..067cf45e3
--- /dev/null
+++ b/Crypto/src/crypto/engines/DesEngine.cs
@@ -0,0 +1,475 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /// <remarks>A class that provides a basic DES engine.</remarks>
+ public class DesEngine
+ : IBlockCipher
+ {
+ internal const int BLOCK_SIZE = 8;
+
+ private int[] workingKey;
+
+ public virtual int[] GetWorkingKey()
+ {
+ return workingKey;
+ }
+
+ /**
+ * initialise a DES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public virtual void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("invalid parameter passed to DES init - " + parameters.GetType().ToString());
+
+ workingKey = GenerateWorkingKey(forEncryption, ((KeyParameter)parameters).GetKey());
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return "DES"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public virtual int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public virtual int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (workingKey == null)
+ throw new InvalidOperationException("DES engine not initialised");
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ DesFunc(workingKey, input, inOff, output, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public virtual void Reset()
+ {
+ }
+
+ /**
+ * what follows is mainly taken from "Applied Cryptography", by
+ * Bruce Schneier, however it also bears great resemblance to Richard
+ * Outerbridge's D3DES...
+ */
+
+// private static readonly short[] Df_Key =
+// {
+// 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
+// 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
+// 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67
+// };
+
+ private static readonly short[] bytebit =
+ {
+ 128, 64, 32, 16, 8, 4, 2, 1
+ };
+
+ private static readonly int[] bigbyte =
+ {
+ 0x800000, 0x400000, 0x200000, 0x100000,
+ 0x80000, 0x40000, 0x20000, 0x10000,
+ 0x8000, 0x4000, 0x2000, 0x1000,
+ 0x800, 0x400, 0x200, 0x100,
+ 0x80, 0x40, 0x20, 0x10,
+ 0x8, 0x4, 0x2, 0x1
+ };
+
+ /*
+ * Use the key schedule specified in the Standard (ANSI X3.92-1981).
+ */
+ private static readonly byte[] pc1 =
+ {
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
+ };
+
+ private static readonly byte[] totrot =
+ {
+ 1, 2, 4, 6, 8, 10, 12, 14,
+ 15, 17, 19, 21, 23, 25, 27, 28
+ };
+
+ private static readonly byte[] pc2 =
+ {
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
+ };
+
+ private static readonly uint[] SP1 =
+ {
+ 0x01010400, 0x00000000, 0x00010000, 0x01010404,
+ 0x01010004, 0x00010404, 0x00000004, 0x00010000,
+ 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+ 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+ 0x00000404, 0x01000400, 0x01000400, 0x00010400,
+ 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+ 0x00010004, 0x01000004, 0x01000004, 0x00010004,
+ 0x00000000, 0x00000404, 0x00010404, 0x01000000,
+ 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+ 0x01010400, 0x01000000, 0x01000000, 0x00000400,
+ 0x01010004, 0x00010000, 0x00010400, 0x01000004,
+ 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+ 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+ 0x01000004, 0x00000404, 0x00010404, 0x01010400,
+ 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+ 0x00010004, 0x00010400, 0x00000000, 0x01010004
+ };
+
+ private static readonly uint[] SP2 =
+ {
+ 0x80108020, 0x80008000, 0x00008000, 0x00108020,
+ 0x00100000, 0x00000020, 0x80100020, 0x80008020,
+ 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+ 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+ 0x00108000, 0x00100020, 0x80008020, 0x00000000,
+ 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+ 0x00100020, 0x80000020, 0x00000000, 0x00108000,
+ 0x00008020, 0x80108000, 0x80100000, 0x00008020,
+ 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+ 0x80008020, 0x80100000, 0x80108000, 0x00008000,
+ 0x80100000, 0x80008000, 0x00000020, 0x80108020,
+ 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+ 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+ 0x00100020, 0x80008020, 0x80000020, 0x00100020,
+ 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+ 0x80000000, 0x80100020, 0x80108020, 0x00108000
+ };
+
+ private static readonly uint[] SP3 =
+ {
+ 0x00000208, 0x08020200, 0x00000000, 0x08020008,
+ 0x08000200, 0x00000000, 0x00020208, 0x08000200,
+ 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+ 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+ 0x08000000, 0x00000008, 0x08020200, 0x00000200,
+ 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+ 0x08000208, 0x00020200, 0x00020000, 0x08000208,
+ 0x00000008, 0x08020208, 0x00000200, 0x08000000,
+ 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+ 0x00020000, 0x08020200, 0x08000200, 0x00000000,
+ 0x00000200, 0x00020008, 0x08020208, 0x08000200,
+ 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+ 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+ 0x00000008, 0x00020208, 0x00020200, 0x08000008,
+ 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+ 0x00020208, 0x00000008, 0x08020008, 0x00020200
+ };
+
+ private static readonly uint[] SP4 =
+ {
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802080, 0x00800081, 0x00800001, 0x00002001,
+ 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+ 0x00000001, 0x00002000, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+ 0x00800081, 0x00000001, 0x00002080, 0x00800080,
+ 0x00002000, 0x00802080, 0x00802081, 0x00000081,
+ 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00000000, 0x00802000,
+ 0x00002080, 0x00800080, 0x00800081, 0x00000001,
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+ 0x00800001, 0x00002001, 0x00802080, 0x00800081,
+ 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002000, 0x00802080
+ };
+
+ private static readonly uint[] SP5 =
+ {
+ 0x00000100, 0x02080100, 0x02080000, 0x42000100,
+ 0x00080000, 0x00000100, 0x40000000, 0x02080000,
+ 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+ 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+ 0x02000000, 0x40080000, 0x40080000, 0x00000000,
+ 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+ 0x42080000, 0x40000100, 0x00000000, 0x42000000,
+ 0x02080100, 0x02000000, 0x42000000, 0x00080100,
+ 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+ 0x40000000, 0x02080000, 0x42000100, 0x40080100,
+ 0x02000100, 0x40000000, 0x42080000, 0x02080100,
+ 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+ 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+ 0x02080000, 0x00000000, 0x40080000, 0x42000000,
+ 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+ 0x00000000, 0x40080000, 0x02080100, 0x40000100
+ };
+
+ private static readonly uint[] SP6 =
+ {
+ 0x20000010, 0x20400000, 0x00004000, 0x20404010,
+ 0x20400000, 0x00000010, 0x20404010, 0x00400000,
+ 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+ 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+ 0x00000000, 0x00400010, 0x20004010, 0x00004000,
+ 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+ 0x20400010, 0x00000000, 0x00404010, 0x20404000,
+ 0x00004010, 0x00404000, 0x20404000, 0x20000000,
+ 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+ 0x20404010, 0x00400000, 0x00004010, 0x20000010,
+ 0x00400000, 0x20004000, 0x20000000, 0x00004010,
+ 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+ 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+ 0x00000010, 0x00004000, 0x20400000, 0x00404010,
+ 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+ 0x20404000, 0x20000000, 0x00400010, 0x20004010
+ };
+
+ private static readonly uint[] SP7 =
+ {
+ 0x00200000, 0x04200002, 0x04000802, 0x00000000,
+ 0x00000800, 0x04000802, 0x00200802, 0x04200800,
+ 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+ 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+ 0x04000800, 0x00200802, 0x00200002, 0x04000800,
+ 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+ 0x04200000, 0x00000800, 0x00000802, 0x04200802,
+ 0x00200800, 0x00000002, 0x04000000, 0x00200800,
+ 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+ 0x04000802, 0x04200002, 0x04200002, 0x00000002,
+ 0x00200002, 0x04000000, 0x04000800, 0x00200000,
+ 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+ 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+ 0x00200800, 0x00000000, 0x00000002, 0x04200802,
+ 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+ 0x04000002, 0x04000800, 0x00000800, 0x00200002
+ };
+
+ private static readonly uint[] SP8 =
+ {
+ 0x10001040, 0x00001000, 0x00040000, 0x10041040,
+ 0x10000000, 0x10001040, 0x00000040, 0x10000000,
+ 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+ 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+ 0x10040000, 0x10000040, 0x10001000, 0x00001040,
+ 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+ 0x00001040, 0x00000000, 0x00000000, 0x10040040,
+ 0x10000040, 0x10001000, 0x00041040, 0x00040000,
+ 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+ 0x00000040, 0x10040040, 0x00001000, 0x00041040,
+ 0x10001000, 0x00000040, 0x10000040, 0x10040000,
+ 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+ 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+ 0x10040000, 0x10001000, 0x10001040, 0x00000000,
+ 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+ 0x00001040, 0x00040040, 0x10000000, 0x10041000
+ };
+
+ /**
+ * Generate an integer based working key based on our secret key
+ * and what we processing we are planning to do.
+ *
+ * Acknowledgements for this routine go to James Gillogly and Phil Karn.
+ * (whoever, and wherever they are!).
+ */
+ protected static int[] GenerateWorkingKey(
+ bool encrypting,
+ byte[] key)
+ {
+ int[] newKey = new int[32];
+ bool[] pc1m = new bool[56];
+ bool[] pcr = new bool[56];
+
+ for (int j = 0; j < 56; j++ )
+ {
+ int l = pc1[j];
+
+ pc1m[j] = ((key[(uint) l >> 3] & bytebit[l & 07]) != 0);
+ }
+
+ for (int i = 0; i < 16; i++)
+ {
+ int l, m, n;
+
+ if (encrypting)
+ {
+ m = i << 1;
+ }
+ else
+ {
+ m = (15 - i) << 1;
+ }
+
+ n = m + 1;
+ newKey[m] = newKey[n] = 0;
+
+ for (int j = 0; j < 28; j++)
+ {
+ l = j + totrot[i];
+ if ( l < 28 )
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 28; j < 56; j++)
+ {
+ l = j + totrot[i];
+ if (l < 56 )
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 0; j < 24; j++)
+ {
+ if (pcr[pc2[j]])
+ {
+ newKey[m] |= bigbyte[j];
+ }
+
+ if (pcr[pc2[j + 24]])
+ {
+ newKey[n] |= bigbyte[j];
+ }
+ }
+ }
+
+ //
+ // store the processed key
+ //
+ for (int i = 0; i != 32; i += 2)
+ {
+ int i1, i2;
+
+ i1 = newKey[i];
+ i2 = newKey[i + 1];
+
+ newKey[i] = (int) ( (uint) ((i1 & 0x00fc0000) << 6) |
+ (uint) ((i1 & 0x00000fc0) << 10) |
+ ((uint) (i2 & 0x00fc0000) >> 10) |
+ ((uint) (i2 & 0x00000fc0) >> 6));
+
+ newKey[i + 1] = (int) ( (uint) ((i1 & 0x0003f000) << 12) |
+ (uint) ((i1 & 0x0000003f) << 16) |
+ ((uint) (i2 & 0x0003f000) >> 4) |
+ (uint) (i2 & 0x0000003f));
+ }
+
+ return newKey;
+ }
+
+ /**
+ * the DES engine.
+ */
+ internal static void DesFunc(
+ int[] wKey,
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ uint left = Pack.BE_To_UInt32(input, inOff);
+ uint right = Pack.BE_To_UInt32(input, inOff + 4);
+ uint work;
+
+ work = ((left >> 4) ^ right) & 0x0f0f0f0f;
+ right ^= work;
+ left ^= (work << 4);
+ work = ((left >> 16) ^ right) & 0x0000ffff;
+ right ^= work;
+ left ^= (work << 16);
+ work = ((right >> 2) ^ left) & 0x33333333;
+ left ^= work;
+ right ^= (work << 2);
+ work = ((right >> 8) ^ left) & 0x00ff00ff;
+ left ^= work;
+ right ^= (work << 8);
+ right = (right << 1) | (right >> 31);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left << 1) | (left >> 31);
+
+ for (int round = 0; round < 8; round++)
+ {
+ uint fval;
+
+ work = (right << 28) | (right >> 4);
+ work ^= (uint)wKey[round * 4 + 0];
+ fval = SP7[work & 0x3f];
+ fval |= SP5[(work >> 8) & 0x3f];
+ fval |= SP3[(work >> 16) & 0x3f];
+ fval |= SP1[(work >> 24) & 0x3f];
+ work = right ^ (uint)wKey[round * 4 + 1];
+ fval |= SP8[ work & 0x3f];
+ fval |= SP6[(work >> 8) & 0x3f];
+ fval |= SP4[(work >> 16) & 0x3f];
+ fval |= SP2[(work >> 24) & 0x3f];
+ left ^= fval;
+ work = (left << 28) | (left >> 4);
+ work ^= (uint)wKey[round * 4 + 2];
+ fval = SP7[ work & 0x3f];
+ fval |= SP5[(work >> 8) & 0x3f];
+ fval |= SP3[(work >> 16) & 0x3f];
+ fval |= SP1[(work >> 24) & 0x3f];
+ work = left ^ (uint)wKey[round * 4 + 3];
+ fval |= SP8[ work & 0x3f];
+ fval |= SP6[(work >> 8) & 0x3f];
+ fval |= SP4[(work >> 16) & 0x3f];
+ fval |= SP2[(work >> 24) & 0x3f];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >> 1);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left << 31) | (left >> 1);
+ work = ((left >> 8) ^ right) & 0x00ff00ff;
+ right ^= work;
+ left ^= (work << 8);
+ work = ((left >> 2) ^ right) & 0x33333333;
+ right ^= work;
+ left ^= (work << 2);
+ work = ((right >> 16) ^ left) & 0x0000ffff;
+ left ^= work;
+ right ^= (work << 16);
+ work = ((right >> 4) ^ left) & 0x0f0f0f0f;
+ left ^= work;
+ right ^= (work << 4);
+
+ Pack.UInt32_To_BE(right, outBytes, outOff);
+ Pack.UInt32_To_BE(left, outBytes, outOff + 4);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/ElGamalEngine.cs b/Crypto/src/crypto/engines/ElGamalEngine.cs
new file mode 100644
index 000000000..3d256a087
--- /dev/null
+++ b/Crypto/src/crypto/engines/ElGamalEngine.cs
@@ -0,0 +1,178 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * this does your basic ElGamal algorithm.
+ */
+ public class ElGamalEngine
+ : IAsymmetricBlockCipher
+ {
+ private ElGamalKeyParameters key;
+ private SecureRandom random;
+ private bool forEncryption;
+ private int bitSize;
+
+ public string AlgorithmName
+ {
+ get { return "ElGamal"; }
+ }
+
+ /**
+ * initialise the ElGamal engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary ElGamal key parameters.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom) parameters;
+
+ this.key = (ElGamalKeyParameters) p.Parameters;
+ this.random = p.Random;
+ }
+ else
+ {
+ this.key = (ElGamalKeyParameters) parameters;
+ this.random = new SecureRandom();
+ }
+
+ this.forEncryption = forEncryption;
+ this.bitSize = key.Parameters.P.BitLength;
+
+ if (forEncryption)
+ {
+ if (!(key is ElGamalPublicKeyParameters))
+ {
+ throw new ArgumentException("ElGamalPublicKeyParameters are required for encryption.");
+ }
+ }
+ else
+ {
+ if (!(key is ElGamalPrivateKeyParameters))
+ {
+ throw new ArgumentException("ElGamalPrivateKeyParameters are required for decryption.");
+ }
+ }
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For ElGamal this is always one byte less than the size of P on
+ * encryption, and twice the length as the size of P on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int GetInputBlockSize()
+ {
+ if (forEncryption)
+ {
+ return (bitSize - 1) / 8;
+ }
+
+ return 2 * ((bitSize + 7) / 8);
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For ElGamal this is always one byte less than the size of P on
+ * decryption, and twice the length as the size of P on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int GetOutputBlockSize()
+ {
+ if (forEncryption)
+ {
+ return 2 * ((bitSize + 7) / 8);
+ }
+
+ return (bitSize - 1) / 8;
+ }
+
+ /**
+ * Process a single block using the basic ElGamal algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param length the length of the data to be processed.
+ * @return the result of the ElGamal process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] ProcessBlock(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (key == null)
+ throw new InvalidOperationException("ElGamal engine not initialised");
+
+ int maxLength = forEncryption
+ ? (bitSize - 1 + 7) / 8
+ : GetInputBlockSize();
+
+ if (length > maxLength)
+ throw new DataLengthException("input too large for ElGamal cipher.\n");
+
+ BigInteger p = key.Parameters.P;
+
+ byte[] output;
+ if (key is ElGamalPrivateKeyParameters) // decryption
+ {
+ int halfLength = length / 2;
+ BigInteger gamma = new BigInteger(1, input, inOff, halfLength);
+ BigInteger phi = new BigInteger(1, input, inOff + halfLength, halfLength);
+
+ ElGamalPrivateKeyParameters priv = (ElGamalPrivateKeyParameters) key;
+
+ // a shortcut, which generally relies on p being prime amongst other things.
+ // if a problem with this shows up, check the p and g values!
+ BigInteger m = gamma.ModPow(p.Subtract(BigInteger.One).Subtract(priv.X), p).Multiply(phi).Mod(p);
+
+ output = m.ToByteArrayUnsigned();
+ }
+ else // encryption
+ {
+ BigInteger tmp = new BigInteger(1, input, inOff, length);
+
+ if (tmp.BitLength >= p.BitLength)
+ throw new DataLengthException("input too large for ElGamal cipher.\n");
+
+
+ ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters) key;
+
+ BigInteger pSub2 = p.Subtract(BigInteger.Two);
+
+ // TODO In theory, a series of 'k', 'g.ModPow(k, p)' and 'y.ModPow(k, p)' can be pre-calculated
+ BigInteger k;
+ do
+ {
+ k = new BigInteger(p.BitLength, random);
+ }
+ while (k.SignValue == 0 || k.CompareTo(pSub2) > 0);
+
+ BigInteger g = key.Parameters.G;
+ BigInteger gamma = g.ModPow(k, p);
+ BigInteger phi = tmp.Multiply(pub.Y.ModPow(k, p)).Mod(p);
+
+ output = new byte[this.GetOutputBlockSize()];
+
+ // TODO Add methods to allow writing BigInteger to existing byte array?
+ byte[] out1 = gamma.ToByteArrayUnsigned();
+ byte[] out2 = phi.ToByteArrayUnsigned();
+ out1.CopyTo(output, output.Length / 2 - out1.Length);
+ out2.CopyTo(output, output.Length - out2.Length);
+ }
+
+ return output;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/GOST28147Engine.cs b/Crypto/src/crypto/engines/GOST28147Engine.cs
new file mode 100644
index 000000000..17593d2c0
--- /dev/null
+++ b/Crypto/src/crypto/engines/GOST28147Engine.cs
@@ -0,0 +1,377 @@
+using System;
+using System.Collections;
+using System.Globalization;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * implementation of GOST 28147-89
+ */
+ public class Gost28147Engine
+ : IBlockCipher
+ {
+ private const int BlockSize = 8;
+ private int[] workingKey = null;
+ private bool forEncryption;
+
+ private byte[] S = Sbox_Default;
+
+ // these are the S-boxes given in Applied Cryptography 2nd Ed., p. 333
+ // This is default S-box!
+ private static readonly byte[] Sbox_Default = {
+ 0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3,
+ 0xE,0xB,0x4,0xC,0x6,0xD,0xF,0xA,0x2,0x3,0x8,0x1,0x0,0x7,0x5,0x9,
+ 0x5,0x8,0x1,0xD,0xA,0x3,0x4,0x2,0xE,0xF,0xC,0x7,0x6,0x0,0x9,0xB,
+ 0x7,0xD,0xA,0x1,0x0,0x8,0x9,0xF,0xE,0x4,0x6,0xC,0xB,0x2,0x5,0x3,
+ 0x6,0xC,0x7,0x1,0x5,0xF,0xD,0x8,0x4,0xA,0x9,0xE,0x0,0x3,0xB,0x2,
+ 0x4,0xB,0xA,0x0,0x7,0x2,0x1,0xD,0x3,0x6,0x8,0x5,0x9,0xC,0xF,0xE,
+ 0xD,0xB,0x4,0x1,0x3,0xF,0x5,0x9,0x0,0xA,0xE,0x7,0x6,0x8,0x2,0xC,
+ 0x1,0xF,0xD,0x0,0x5,0x7,0xA,0x4,0x9,0x2,0x3,0xE,0x6,0xB,0x8,0xC
+ };
+
+ /*
+ * class content S-box parameters for encrypting
+ * getting from, see: http://tools.ietf.org/id/draft-popov-cryptopro-cpalgs-01.txt
+ * http://tools.ietf.org/id/draft-popov-cryptopro-cpalgs-02.txt
+ */
+ private static readonly byte[] ESbox_Test = {
+ 0x4,0x2,0xF,0x5,0x9,0x1,0x0,0x8,0xE,0x3,0xB,0xC,0xD,0x7,0xA,0x6,
+ 0xC,0x9,0xF,0xE,0x8,0x1,0x3,0xA,0x2,0x7,0x4,0xD,0x6,0x0,0xB,0x5,
+ 0xD,0x8,0xE,0xC,0x7,0x3,0x9,0xA,0x1,0x5,0x2,0x4,0x6,0xF,0x0,0xB,
+ 0xE,0x9,0xB,0x2,0x5,0xF,0x7,0x1,0x0,0xD,0xC,0x6,0xA,0x4,0x3,0x8,
+ 0x3,0xE,0x5,0x9,0x6,0x8,0x0,0xD,0xA,0xB,0x7,0xC,0x2,0x1,0xF,0x4,
+ 0x8,0xF,0x6,0xB,0x1,0x9,0xC,0x5,0xD,0x3,0x7,0xA,0x0,0xE,0x2,0x4,
+ 0x9,0xB,0xC,0x0,0x3,0x6,0x7,0x5,0x4,0x8,0xE,0xF,0x1,0xA,0x2,0xD,
+ 0xC,0x6,0x5,0x2,0xB,0x0,0x9,0xD,0x3,0xE,0x7,0xA,0xF,0x4,0x1,0x8
+ };
+
+ private static readonly byte[] ESbox_A = {
+ 0x9,0x6,0x3,0x2,0x8,0xB,0x1,0x7,0xA,0x4,0xE,0xF,0xC,0x0,0xD,0x5,
+ 0x3,0x7,0xE,0x9,0x8,0xA,0xF,0x0,0x5,0x2,0x6,0xC,0xB,0x4,0xD,0x1,
+ 0xE,0x4,0x6,0x2,0xB,0x3,0xD,0x8,0xC,0xF,0x5,0xA,0x0,0x7,0x1,0x9,
+ 0xE,0x7,0xA,0xC,0xD,0x1,0x3,0x9,0x0,0x2,0xB,0x4,0xF,0x8,0x5,0x6,
+ 0xB,0x5,0x1,0x9,0x8,0xD,0xF,0x0,0xE,0x4,0x2,0x3,0xC,0x7,0xA,0x6,
+ 0x3,0xA,0xD,0xC,0x1,0x2,0x0,0xB,0x7,0x5,0x9,0x4,0x8,0xF,0xE,0x6,
+ 0x1,0xD,0x2,0x9,0x7,0xA,0x6,0x0,0x8,0xC,0x4,0x5,0xF,0x3,0xB,0xE,
+ 0xB,0xA,0xF,0x5,0x0,0xC,0xE,0x8,0x6,0x2,0x3,0x9,0x1,0x7,0xD,0x4
+ };
+
+ private static readonly byte[] ESbox_B = {
+ 0x8,0x4,0xB,0x1,0x3,0x5,0x0,0x9,0x2,0xE,0xA,0xC,0xD,0x6,0x7,0xF,
+ 0x0,0x1,0x2,0xA,0x4,0xD,0x5,0xC,0x9,0x7,0x3,0xF,0xB,0x8,0x6,0xE,
+ 0xE,0xC,0x0,0xA,0x9,0x2,0xD,0xB,0x7,0x5,0x8,0xF,0x3,0x6,0x1,0x4,
+ 0x7,0x5,0x0,0xD,0xB,0x6,0x1,0x2,0x3,0xA,0xC,0xF,0x4,0xE,0x9,0x8,
+ 0x2,0x7,0xC,0xF,0x9,0x5,0xA,0xB,0x1,0x4,0x0,0xD,0x6,0x8,0xE,0x3,
+ 0x8,0x3,0x2,0x6,0x4,0xD,0xE,0xB,0xC,0x1,0x7,0xF,0xA,0x0,0x9,0x5,
+ 0x5,0x2,0xA,0xB,0x9,0x1,0xC,0x3,0x7,0x4,0xD,0x0,0x6,0xF,0x8,0xE,
+ 0x0,0x4,0xB,0xE,0x8,0x3,0x7,0x1,0xA,0x2,0x9,0x6,0xF,0xD,0x5,0xC
+ };
+
+ private static readonly byte[] ESbox_C = {
+ 0x1,0xB,0xC,0x2,0x9,0xD,0x0,0xF,0x4,0x5,0x8,0xE,0xA,0x7,0x6,0x3,
+ 0x0,0x1,0x7,0xD,0xB,0x4,0x5,0x2,0x8,0xE,0xF,0xC,0x9,0xA,0x6,0x3,
+ 0x8,0x2,0x5,0x0,0x4,0x9,0xF,0xA,0x3,0x7,0xC,0xD,0x6,0xE,0x1,0xB,
+ 0x3,0x6,0x0,0x1,0x5,0xD,0xA,0x8,0xB,0x2,0x9,0x7,0xE,0xF,0xC,0x4,
+ 0x8,0xD,0xB,0x0,0x4,0x5,0x1,0x2,0x9,0x3,0xC,0xE,0x6,0xF,0xA,0x7,
+ 0xC,0x9,0xB,0x1,0x8,0xE,0x2,0x4,0x7,0x3,0x6,0x5,0xA,0x0,0xF,0xD,
+ 0xA,0x9,0x6,0x8,0xD,0xE,0x2,0x0,0xF,0x3,0x5,0xB,0x4,0x1,0xC,0x7,
+ 0x7,0x4,0x0,0x5,0xA,0x2,0xF,0xE,0xC,0x6,0x1,0xB,0xD,0x9,0x3,0x8
+ };
+
+ private static readonly byte[] ESbox_D = {
+ 0xF,0xC,0x2,0xA,0x6,0x4,0x5,0x0,0x7,0x9,0xE,0xD,0x1,0xB,0x8,0x3,
+ 0xB,0x6,0x3,0x4,0xC,0xF,0xE,0x2,0x7,0xD,0x8,0x0,0x5,0xA,0x9,0x1,
+ 0x1,0xC,0xB,0x0,0xF,0xE,0x6,0x5,0xA,0xD,0x4,0x8,0x9,0x3,0x7,0x2,
+ 0x1,0x5,0xE,0xC,0xA,0x7,0x0,0xD,0x6,0x2,0xB,0x4,0x9,0x3,0xF,0x8,
+ 0x0,0xC,0x8,0x9,0xD,0x2,0xA,0xB,0x7,0x3,0x6,0x5,0x4,0xE,0xF,0x1,
+ 0x8,0x0,0xF,0x3,0x2,0x5,0xE,0xB,0x1,0xA,0x4,0x7,0xC,0x9,0xD,0x6,
+ 0x3,0x0,0x6,0xF,0x1,0xE,0x9,0x2,0xD,0x8,0xC,0x4,0xB,0xA,0x5,0x7,
+ 0x1,0xA,0x6,0x8,0xF,0xB,0x0,0x4,0xC,0x3,0x5,0x9,0x7,0xD,0x2,0xE
+ };
+
+ //S-box for digest
+ private static readonly byte[] DSbox_Test = {
+ 0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3,
+ 0xE,0xB,0x4,0xC,0x6,0xD,0xF,0xA,0x2,0x3,0x8,0x1,0x0,0x7,0x5,0x9,
+ 0x5,0x8,0x1,0xD,0xA,0x3,0x4,0x2,0xE,0xF,0xC,0x7,0x6,0x0,0x9,0xB,
+ 0x7,0xD,0xA,0x1,0x0,0x8,0x9,0xF,0xE,0x4,0x6,0xC,0xB,0x2,0x5,0x3,
+ 0x6,0xC,0x7,0x1,0x5,0xF,0xD,0x8,0x4,0xA,0x9,0xE,0x0,0x3,0xB,0x2,
+ 0x4,0xB,0xA,0x0,0x7,0x2,0x1,0xD,0x3,0x6,0x8,0x5,0x9,0xC,0xF,0xE,
+ 0xD,0xB,0x4,0x1,0x3,0xF,0x5,0x9,0x0,0xA,0xE,0x7,0x6,0x8,0x2,0xC,
+ 0x1,0xF,0xD,0x0,0x5,0x7,0xA,0x4,0x9,0x2,0x3,0xE,0x6,0xB,0x8,0xC
+ };
+
+ private static readonly byte[] DSbox_A = {
+ 0xA,0x4,0x5,0x6,0x8,0x1,0x3,0x7,0xD,0xC,0xE,0x0,0x9,0x2,0xB,0xF,
+ 0x5,0xF,0x4,0x0,0x2,0xD,0xB,0x9,0x1,0x7,0x6,0x3,0xC,0xE,0xA,0x8,
+ 0x7,0xF,0xC,0xE,0x9,0x4,0x1,0x0,0x3,0xB,0x5,0x2,0x6,0xA,0x8,0xD,
+ 0x4,0xA,0x7,0xC,0x0,0xF,0x2,0x8,0xE,0x1,0x6,0x5,0xD,0xB,0x9,0x3,
+ 0x7,0x6,0x4,0xB,0x9,0xC,0x2,0xA,0x1,0x8,0x0,0xE,0xF,0xD,0x3,0x5,
+ 0x7,0x6,0x2,0x4,0xD,0x9,0xF,0x0,0xA,0x1,0x5,0xB,0x8,0xE,0xC,0x3,
+ 0xD,0xE,0x4,0x1,0x7,0x0,0x5,0xA,0x3,0xC,0x8,0xF,0x6,0x2,0x9,0xB,
+ 0x1,0x3,0xA,0x9,0x5,0xB,0x4,0xF,0x8,0x6,0x7,0xE,0xD,0x0,0x2,0xC
+ };
+
+ //
+ // pre-defined sbox table
+ //
+ private static readonly IDictionary sBoxes = Platform.CreateHashtable();
+
+ static Gost28147Engine()
+ {
+ AddSBox("Default", Sbox_Default);
+ AddSBox("E-TEST", ESbox_Test);
+ AddSBox("E-A", ESbox_A);
+ AddSBox("E-B", ESbox_B);
+ AddSBox("E-C", ESbox_C);
+ AddSBox("E-D", ESbox_D);
+ AddSBox("D-TEST", DSbox_Test);
+ AddSBox("D-A", DSbox_A);
+ }
+
+ private static void AddSBox(string sBoxName, byte[] sBox)
+ {
+ sBoxes.Add(sBoxName.ToUpperInvariant(), sBox);
+ }
+
+ /**
+ * standard constructor.
+ */
+ public Gost28147Engine()
+ {
+ }
+
+ /**
+ * initialise an Gost28147 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithSBox)
+ {
+ ParametersWithSBox param = (ParametersWithSBox)parameters;
+
+ //
+ // Set the S-Box
+ //
+ byte[] sBox = param.GetSBox();
+ if (sBox.Length != Sbox_Default.Length)
+ throw new ArgumentException("invalid S-box passed to GOST28147 init");
+
+ this.S = Arrays.Clone(sBox);
+
+ //
+ // set key if there is one
+ //
+ if (param.Parameters != null)
+ {
+ workingKey = generateWorkingKey(forEncryption,
+ ((KeyParameter)param.Parameters).GetKey());
+ }
+ }
+ else if (parameters is KeyParameter)
+ {
+ workingKey = generateWorkingKey(forEncryption,
+ ((KeyParameter)parameters).GetKey());
+ }
+ else if (parameters != null)
+ {
+ throw new ArgumentException("invalid parameter passed to Gost28147 init - " + parameters.GetType().Name);
+ }
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Gost28147"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BlockSize;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new InvalidOperationException("Gost28147 engine not initialised");
+ }
+
+ if ((inOff + BlockSize) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BlockSize) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ Gost28147Func(workingKey, input, inOff, output, outOff);
+
+ return BlockSize;
+ }
+
+ public void Reset()
+ {
+ }
+
+ private int[] generateWorkingKey(
+ bool forEncryption,
+ byte[] userKey)
+ {
+ this.forEncryption = forEncryption;
+
+ if (userKey.Length != 32)
+ {
+ throw new ArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!");
+ }
+
+ int[] key = new int[8];
+ for(int i=0; i!=8; i++)
+ {
+ key[i] = bytesToint(userKey,i*4);
+ }
+
+ return key;
+ }
+
+ private int Gost28147_mainStep(int n1, int key)
+ {
+ int cm = (key + n1); // CM1
+
+ // S-box replacing
+
+ int om = S[ 0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4);
+ om += S[ 16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4);
+ om += S[ 32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4);
+ om += S[ 48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4);
+ om += S[ 64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4);
+ om += S[ 80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4);
+ om += S[ 96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4);
+ om += S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4);
+
+// return om << 11 | om >>> (32-11); // 11-leftshift
+ int omLeft = om << 11;
+ int omRight = (int)(((uint) om) >> (32 - 11)); // Note: Casts required to get unsigned bit rotation
+
+ return omLeft | omRight;
+ }
+
+ private void Gost28147Func(
+ int[] workingKey,
+ byte[] inBytes,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ int N1, N2, tmp; //tmp -> for saving N1
+ N1 = bytesToint(inBytes, inOff);
+ N2 = bytesToint(inBytes, inOff + 4);
+
+ if (this.forEncryption)
+ {
+ for(int k = 0; k < 3; k++) // 1-24 steps
+ {
+ for(int j = 0; j < 8; j++)
+ {
+ tmp = N1;
+ int step = Gost28147_mainStep(N1, workingKey[j]);
+ N1 = N2 ^ step; // CM2
+ N2 = tmp;
+ }
+ }
+ for(int j = 7; j > 0; j--) // 25-31 steps
+ {
+ tmp = N1;
+ N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+ else //decrypt
+ {
+ for(int j = 0; j < 8; j++) // 1-8 steps
+ {
+ tmp = N1;
+ N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ for(int k = 0; k < 3; k++) //9-31 steps
+ {
+ for(int j = 7; j >= 0; j--)
+ {
+ if ((k == 2) && (j==0))
+ {
+ break; // break 32 step
+ }
+ tmp = N1;
+ N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+ }
+
+ N2 = N2 ^ Gost28147_mainStep(N1, workingKey[0]); // 32 step (N1=N1)
+
+ intTobytes(N1, outBytes, outOff);
+ intTobytes(N2, outBytes, outOff + 4);
+ }
+
+ //array of bytes to type int
+ private static int bytesToint(
+ byte[] inBytes,
+ int inOff)
+ {
+ return (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) +
+ ((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff);
+ }
+
+ //int to array of bytes
+ private static void intTobytes(
+ int num,
+ byte[] outBytes,
+ int outOff)
+ {
+ outBytes[outOff + 3] = (byte)(num >> 24);
+ outBytes[outOff + 2] = (byte)(num >> 16);
+ outBytes[outOff + 1] = (byte)(num >> 8);
+ outBytes[outOff] = (byte)num;
+ }
+
+ /**
+ * Return the S-Box associated with SBoxName
+ * @param sBoxName name of the S-Box
+ * @return byte array representing the S-Box
+ */
+ public static byte[] GetSBox(
+ string sBoxName)
+ {
+ byte[] sBox = (byte[])sBoxes[sBoxName.ToUpperInvariant()];
+
+ if (sBox == null)
+ {
+ throw new ArgumentException("Unknown S-Box - possible types: "
+ + "\"Default\", \"E-Test\", \"E-A\", \"E-B\", \"E-C\", \"E-D\", \"D-Test\", \"D-A\".");
+ }
+
+ return Arrays.Clone(sBox);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/HC128Engine.cs b/Crypto/src/crypto/engines/HC128Engine.cs
new file mode 100644
index 000000000..a2d099f87
--- /dev/null
+++ b/Crypto/src/crypto/engines/HC128Engine.cs
@@ -0,0 +1,235 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * HC-128 is a software-efficient stream cipher created by Hongjun Wu. It
+ * generates keystream from a 128-bit secret key and a 128-bit initialization
+ * vector.
+ * <p>
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf
+ * </p><p>
+ * It is a third phase candidate in the eStream contest, and is patent-free.
+ * No attacks are known as of today (April 2007). See
+ *
+ * http://www.ecrypt.eu.org/stream/hcp3.html
+ * </p>
+ */
+ public class HC128Engine
+ : IStreamCipher
+ {
+ private uint[] p = new uint[512];
+ private uint[] q = new uint[512];
+ private uint cnt = 0;
+
+ private static uint F1(uint x)
+ {
+ return RotateRight(x, 7) ^ RotateRight(x, 18) ^ (x >> 3);
+ }
+
+ private static uint F2(uint x)
+ {
+ return RotateRight(x, 17) ^ RotateRight(x, 19) ^ (x >> 10);
+ }
+
+ private uint G1(uint x, uint y, uint z)
+ {
+ return (RotateRight(x, 10) ^ RotateRight(z, 23)) + RotateRight(y, 8);
+ }
+
+ private uint G2(uint x, uint y, uint z)
+ {
+ return (RotateLeft(x, 10) ^ RotateLeft(z, 23)) + RotateLeft(y, 8);
+ }
+
+ private static uint RotateLeft(uint x, int bits)
+ {
+ return (x << bits) | (x >> -bits);
+ }
+
+ private static uint RotateRight(uint x, int bits)
+ {
+ return (x >> bits) | (x << -bits);
+ }
+
+ private uint H1(uint x)
+ {
+ return q[x & 0xFF] + q[((x >> 16) & 0xFF) + 256];
+ }
+
+ private uint H2(uint x)
+ {
+ return p[x & 0xFF] + p[((x >> 16) & 0xFF) + 256];
+ }
+
+ private static uint Mod1024(uint x)
+ {
+ return x & 0x3FF;
+ }
+
+ private static uint Mod512(uint x)
+ {
+ return x & 0x1FF;
+ }
+
+ private static uint Dim(uint x, uint y)
+ {
+ return Mod512(x - y);
+ }
+
+ private uint Step()
+ {
+ uint j = Mod512(cnt);
+ uint ret;
+ if (cnt < 512)
+ {
+ p[j] += G1(p[Dim(j, 3)], p[Dim(j, 10)], p[Dim(j, 511)]);
+ ret = H1(p[Dim(j, 12)]) ^ p[j];
+ }
+ else
+ {
+ q[j] += G2(q[Dim(j, 3)], q[Dim(j, 10)], q[Dim(j, 511)]);
+ ret = H2(q[Dim(j, 12)]) ^ q[j];
+ }
+ cnt = Mod1024(cnt + 1);
+ return ret;
+ }
+
+ private byte[] key, iv;
+ private bool initialised;
+
+ private void Init()
+ {
+ if (key.Length != 16)
+ throw new ArgumentException("The key must be 128 bits long");
+
+ cnt = 0;
+
+ uint[] w = new uint[1280];
+
+ for (int i = 0; i < 16; i++)
+ {
+ w[i >> 2] |= ((uint)key[i] << (8 * (i & 0x3)));
+ }
+ Array.Copy(w, 0, w, 4, 4);
+
+ for (int i = 0; i < iv.Length && i < 16; i++)
+ {
+ w[(i >> 2) + 8] |= ((uint)iv[i] << (8 * (i & 0x3)));
+ }
+ Array.Copy(w, 8, w, 12, 4);
+
+ for (uint i = 16; i < 1280; i++)
+ {
+ w[i] = F2(w[i - 2]) + w[i - 7] + F1(w[i - 15]) + w[i - 16] + i;
+ }
+
+ Array.Copy(w, 256, p, 0, 512);
+ Array.Copy(w, 768, q, 0, 512);
+
+ for (int i = 0; i < 512; i++)
+ {
+ p[i] = Step();
+ }
+ for (int i = 0; i < 512; i++)
+ {
+ q[i] = Step();
+ }
+
+ cnt = 0;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "HC-128"; }
+ }
+
+ /**
+ * Initialise a HC-128 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption. Irrelevant, as
+ * encryption and decryption are the same.
+ * @param params the parameters required to set up the cipher.
+ * @throws ArgumentException if the params argument is
+ * inappropriate (ie. the key is not 128 bit long).
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ ICipherParameters keyParam = parameters;
+
+ if (parameters is ParametersWithIV)
+ {
+ iv = ((ParametersWithIV)parameters).GetIV();
+ keyParam = ((ParametersWithIV)parameters).Parameters;
+ }
+ else
+ {
+ iv = new byte[0];
+ }
+
+ if (keyParam is KeyParameter)
+ {
+ key = ((KeyParameter)keyParam).GetKey();
+ Init();
+ }
+ else
+ {
+ throw new ArgumentException(
+ "Invalid parameter passed to HC128 init - " + parameters.GetType().Name,
+ "parameters");
+ }
+
+ initialised = true;
+ }
+
+ private byte[] buf = new byte[4];
+ private int idx = 0;
+
+ private byte GetByte()
+ {
+ if (idx == 0)
+ {
+ Pack.UInt32_To_LE(Step(), buf);
+ }
+ byte ret = buf[idx];
+ idx = idx + 1 & 0x3;
+ return ret;
+ }
+
+ public void ProcessBytes(
+ byte[] input,
+ int inOff,
+ int len,
+ byte[] output,
+ int outOff)
+ {
+ if (!initialised)
+ throw new InvalidOperationException(AlgorithmName + " not initialised");
+ if ((inOff + len) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + len) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ for (int i = 0; i < len; i++)
+ {
+ output[outOff + i] = (byte)(input[inOff + i] ^ GetByte());
+ }
+ }
+
+ public void Reset()
+ {
+ idx = 0;
+ Init();
+ }
+
+ public byte ReturnByte(byte input)
+ {
+ return (byte)(input ^ GetByte());
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/HC256Engine.cs b/Crypto/src/crypto/engines/HC256Engine.cs
new file mode 100644
index 000000000..da717dab7
--- /dev/null
+++ b/Crypto/src/crypto/engines/HC256Engine.cs
@@ -0,0 +1,224 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * HC-256 is a software-efficient stream cipher created by Hongjun Wu. It
+ * generates keystream from a 256-bit secret key and a 256-bit initialization
+ * vector.
+ * <p>
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
+ * </p><p>
+ * Its brother, HC-128, is a third phase candidate in the eStream contest.
+ * The algorithm is patent-free. No attacks are known as of today (April 2007).
+ * See
+ *
+ * http://www.ecrypt.eu.org/stream/hcp3.html
+ * </p>
+ */
+ public class HC256Engine
+ : IStreamCipher
+ {
+ private uint[] p = new uint[1024];
+ private uint[] q = new uint[1024];
+ private uint cnt = 0;
+
+ private uint Step()
+ {
+ uint j = cnt & 0x3FF;
+ uint ret;
+ if (cnt < 1024)
+ {
+ uint x = p[(j - 3 & 0x3FF)];
+ uint y = p[(j - 1023 & 0x3FF)];
+ p[j] += p[(j - 10 & 0x3FF)]
+ + (RotateRight(x, 10) ^ RotateRight(y, 23))
+ + q[((x ^ y) & 0x3FF)];
+
+ x = p[(j - 12 & 0x3FF)];
+ ret = (q[x & 0xFF] + q[((x >> 8) & 0xFF) + 256]
+ + q[((x >> 16) & 0xFF) + 512] + q[((x >> 24) & 0xFF) + 768])
+ ^ p[j];
+ }
+ else
+ {
+ uint x = q[(j - 3 & 0x3FF)];
+ uint y = q[(j - 1023 & 0x3FF)];
+ q[j] += q[(j - 10 & 0x3FF)]
+ + (RotateRight(x, 10) ^ RotateRight(y, 23))
+ + p[((x ^ y) & 0x3FF)];
+
+ x = q[(j - 12 & 0x3FF)];
+ ret = (p[x & 0xFF] + p[((x >> 8) & 0xFF) + 256]
+ + p[((x >> 16) & 0xFF) + 512] + p[((x >> 24) & 0xFF) + 768])
+ ^ q[j];
+ }
+ cnt = cnt + 1 & 0x7FF;
+ return ret;
+ }
+
+ private byte[] key, iv;
+ private bool initialised;
+
+ private void Init()
+ {
+ if (key.Length != 32 && key.Length != 16)
+ throw new ArgumentException("The key must be 128/256 bits long");
+
+ if (iv.Length < 16)
+ throw new ArgumentException("The IV must be at least 128 bits long");
+
+ if (key.Length != 32)
+ {
+ byte[] k = new byte[32];
+
+ Array.Copy(key, 0, k, 0, key.Length);
+ Array.Copy(key, 0, k, 16, key.Length);
+
+ key = k;
+ }
+
+ if (iv.Length < 32)
+ {
+ byte[] newIV = new byte[32];
+
+ Array.Copy(iv, 0, newIV, 0, iv.Length);
+ Array.Copy(iv, 0, newIV, iv.Length, newIV.Length - iv.Length);
+
+ iv = newIV;
+ }
+
+ cnt = 0;
+
+ uint[] w = new uint[2560];
+
+ for (int i = 0; i < 32; i++)
+ {
+ w[i >> 2] |= ((uint)key[i] << (8 * (i & 0x3)));
+ }
+
+ for (int i = 0; i < 32; i++)
+ {
+ w[(i >> 2) + 8] |= ((uint)iv[i] << (8 * (i & 0x3)));
+ }
+
+ for (uint i = 16; i < 2560; i++)
+ {
+ uint x = w[i - 2];
+ uint y = w[i - 15];
+ w[i] = (RotateRight(x, 17) ^ RotateRight(x, 19) ^ (x >> 10))
+ + w[i - 7]
+ + (RotateRight(y, 7) ^ RotateRight(y, 18) ^ (y >> 3))
+ + w[i - 16] + i;
+ }
+
+ Array.Copy(w, 512, p, 0, 1024);
+ Array.Copy(w, 1536, q, 0, 1024);
+
+ for (int i = 0; i < 4096; i++)
+ {
+ Step();
+ }
+
+ cnt = 0;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "HC-256"; }
+ }
+
+ /**
+ * Initialise a HC-256 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption. Irrelevant, as
+ * encryption and decryption are the same.
+ * @param params the parameters required to set up the cipher.
+ * @throws ArgumentException if the params argument is
+ * inappropriate (ie. the key is not 256 bit long).
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ ICipherParameters keyParam = parameters;
+
+ if (parameters is ParametersWithIV)
+ {
+ iv = ((ParametersWithIV)parameters).GetIV();
+ keyParam = ((ParametersWithIV)parameters).Parameters;
+ }
+ else
+ {
+ iv = new byte[0];
+ }
+
+ if (keyParam is KeyParameter)
+ {
+ key = ((KeyParameter)keyParam).GetKey();
+ Init();
+ }
+ else
+ {
+ throw new ArgumentException(
+ "Invalid parameter passed to HC256 init - " + parameters.GetType().Name,
+ "parameters");
+ }
+
+ initialised = true;
+ }
+
+ private byte[] buf = new byte[4];
+ private int idx = 0;
+
+ private byte GetByte()
+ {
+ if (idx == 0)
+ {
+ Pack.UInt32_To_LE(Step(), buf);
+ }
+ byte ret = buf[idx];
+ idx = idx + 1 & 0x3;
+ return ret;
+ }
+
+ public void ProcessBytes(
+ byte[] input,
+ int inOff,
+ int len,
+ byte[] output,
+ int outOff)
+ {
+ if (!initialised)
+ throw new InvalidOperationException(AlgorithmName + " not initialised");
+ if ((inOff + len) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + len) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ for (int i = 0; i < len; i++)
+ {
+ output[outOff + i] = (byte)(input[inOff + i] ^ GetByte());
+ }
+ }
+
+ public void Reset()
+ {
+ idx = 0;
+ Init();
+ }
+
+ public byte ReturnByte(byte input)
+ {
+ return (byte)(input ^ GetByte());
+ }
+
+ private static uint RotateRight(uint x, int bits)
+ {
+ return (x >> bits) | (x << -bits);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/ISAACEngine.cs b/Crypto/src/crypto/engines/ISAACEngine.cs
new file mode 100644
index 000000000..1120a4104
--- /dev/null
+++ b/Crypto/src/crypto/engines/ISAACEngine.cs
@@ -0,0 +1,252 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * Implementation of Bob Jenkin's ISAAC (Indirection Shift Accumulate Add and Count).
+ * see: http://www.burtleburtle.net/bob/rand/isaacafa.html
+ */
+ public class IsaacEngine
+ : IStreamCipher
+ {
+ // Constants
+ private static readonly int sizeL = 8,
+ stateArraySize = sizeL<<5; // 256
+
+ // Cipher's internal state
+ private uint[] engineState = null, // mm
+ results = null; // randrsl
+ private uint a = 0, b = 0, c = 0;
+
+ // Engine state
+ private int index = 0;
+ private byte[] keyStream = new byte[stateArraySize<<2], // results expanded into bytes
+ workingKey = null;
+ private bool initialised = false;
+
+ /**
+ * initialise an ISAAC cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception ArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException(
+ "invalid parameter passed to ISAAC Init - " + parameters.GetType().Name,
+ "parameters");
+
+ /*
+ * ISAAC encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant.
+ */
+ KeyParameter p = (KeyParameter) parameters;
+ setKey(p.GetKey());
+ }
+
+ public byte ReturnByte(
+ byte input)
+ {
+ if (index == 0)
+ {
+ isaac();
+ keyStream = intToByteLittle(results);
+ }
+
+ byte output = (byte)(keyStream[index]^input);
+ index = (index + 1) & 1023;
+
+ return output;
+ }
+
+ public void ProcessBytes(
+ byte[] input,
+ int inOff,
+ int len,
+ byte[] output,
+ int outOff)
+ {
+ if (!initialised)
+ throw new InvalidOperationException(AlgorithmName + " not initialised");
+ if ((inOff + len) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + len) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ for (int i = 0; i < len; i++)
+ {
+ if (index == 0)
+ {
+ isaac();
+ keyStream = intToByteLittle(results);
+ }
+ output[i+outOff] = (byte)(keyStream[index]^input[i+inOff]);
+ index = (index + 1) & 1023;
+ }
+ }
+
+ public string AlgorithmName
+ {
+ get { return "ISAAC"; }
+ }
+
+ public void Reset()
+ {
+ setKey(workingKey);
+ }
+
+ // Private implementation
+ private void setKey(
+ byte[] keyBytes)
+ {
+ workingKey = keyBytes;
+
+ if (engineState == null)
+ {
+ engineState = new uint[stateArraySize];
+ }
+
+ if (results == null)
+ {
+ results = new uint[stateArraySize];
+ }
+
+ int i, j, k;
+
+ // Reset state
+ for (i = 0; i < stateArraySize; i++)
+ {
+ engineState[i] = results[i] = 0;
+ }
+ a = b = c = 0;
+
+ // Reset index counter for output
+ index = 0;
+
+ // Convert the key bytes to ints and put them into results[] for initialization
+ byte[] t = new byte[keyBytes.Length + (keyBytes.Length & 3)];
+ Array.Copy(keyBytes, 0, t, 0, keyBytes.Length);
+ for (i = 0; i < t.Length; i+=4)
+ {
+ results[i>>2] = byteToIntLittle(t, i);
+ }
+
+ // It has begun?
+ uint[] abcdefgh = new uint[sizeL];
+
+ for (i = 0; i < sizeL; i++)
+ {
+ abcdefgh[i] = 0x9e3779b9; // Phi (golden ratio)
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ mix(abcdefgh);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < stateArraySize; j+=sizeL)
+ {
+ for (k = 0; k < sizeL; k++)
+ {
+ abcdefgh[k] += (i<1) ? results[j+k] : engineState[j+k];
+ }
+
+ mix(abcdefgh);
+
+ for (k = 0; k < sizeL; k++)
+ {
+ engineState[j+k] = abcdefgh[k];
+ }
+ }
+ }
+
+ isaac();
+
+ initialised = true;
+ }
+
+ private void isaac()
+ {
+ uint x, y;
+
+ b += ++c;
+ for (int i = 0; i < stateArraySize; i++)
+ {
+ x = engineState[i];
+ switch (i & 3)
+ {
+ case 0: a ^= (a << 13); break;
+ case 1: a ^= (a >> 6); break;
+ case 2: a ^= (a << 2); break;
+ case 3: a ^= (a >> 16); break;
+ }
+ a += engineState[(i+128) & 0xFF];
+ engineState[i] = y = engineState[(int)((uint)x >> 2) & 0xFF] + a + b;
+ results[i] = b = engineState[(int)((uint)y >> 10) & 0xFF] + x;
+ }
+ }
+
+ private void mix(uint[] x)
+ {
+// x[0]^=x[1]<< 11; x[3]+=x[0]; x[1]+=x[2];
+// x[1]^=x[2]>>> 2; x[4]+=x[1]; x[2]+=x[3];
+// x[2]^=x[3]<< 8; x[5]+=x[2]; x[3]+=x[4];
+// x[3]^=x[4]>>>16; x[6]+=x[3]; x[4]+=x[5];
+// x[4]^=x[5]<< 10; x[7]+=x[4]; x[5]+=x[6];
+// x[5]^=x[6]>>> 4; x[0]+=x[5]; x[6]+=x[7];
+// x[6]^=x[7]<< 8; x[1]+=x[6]; x[7]+=x[0];
+// x[7]^=x[0]>>> 9; x[2]+=x[7]; x[0]+=x[1];
+ x[0]^=x[1]<< 11; x[3]+=x[0]; x[1]+=x[2];
+ x[1]^=x[2]>> 2; x[4]+=x[1]; x[2]+=x[3];
+ x[2]^=x[3]<< 8; x[5]+=x[2]; x[3]+=x[4];
+ x[3]^=x[4]>> 16; x[6]+=x[3]; x[4]+=x[5];
+ x[4]^=x[5]<< 10; x[7]+=x[4]; x[5]+=x[6];
+ x[5]^=x[6]>> 4; x[0]+=x[5]; x[6]+=x[7];
+ x[6]^=x[7]<< 8; x[1]+=x[6]; x[7]+=x[0];
+ x[7]^=x[0]>> 9; x[2]+=x[7]; x[0]+=x[1];
+ }
+
+ private uint byteToIntLittle(
+ byte[] x,
+ int offset)
+ {
+ uint result = (byte) x[offset + 3];
+ result = (result << 8) | x[offset + 2];
+ result = (result << 8) | x[offset + 1];
+ result = (result << 8) | x[offset + 0];
+ return result;
+ }
+
+ private byte[] intToByteLittle(
+ uint x)
+ {
+ byte[] output = new byte[4];
+ output[3] = (byte)x;
+ output[2] = (byte)(x >> 8);
+ output[1] = (byte)(x >> 16);
+ output[0] = (byte)(x >> 24);
+ return output;
+ }
+
+ private byte[] intToByteLittle(
+ uint[] x)
+ {
+ byte[] output = new byte[4*x.Length];
+ for (int i = 0, j = 0; i < x.Length; i++,j+=4)
+ {
+ Array.Copy(intToByteLittle(x[i]), 0, output, j, 4);
+ }
+ return output;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/IdeaEngine.cs b/Crypto/src/crypto/engines/IdeaEngine.cs
new file mode 100644
index 000000000..f763c5939
--- /dev/null
+++ b/Crypto/src/crypto/engines/IdeaEngine.cs
@@ -0,0 +1,341 @@
+#if INCLUDE_IDEA
+
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * A class that provides a basic International Data Encryption Algorithm (IDEA) engine.
+ * <p>
+ * This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM"
+ * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (baring 1 typo at the
+ * end of the mulinv function!).
+ * </p>
+ * <p>
+ * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/
+ * </p>
+ * <p>
+ * Note 1: This algorithm is patented in the USA, Japan, and Europe including
+ * at least Austria, France, Germany, Italy, Netherlands, Spain, Sweden, Switzerland
+ * and the United Kingdom. Non-commercial use is free, however any commercial
+ * products are liable for royalties. Please see
+ * <a href="http://www.mediacrypt.com">www.mediacrypt.com</a> for
+ * further details. This announcement has been included at the request of
+ * the patent holders.
+ * </p>
+ * <p>
+ * Note 2: Due to the requests concerning the above, this algorithm is now only
+ * included in the extended assembly. It is not included in the default distributions.
+ * </p>
+ */
+ public class IdeaEngine
+ : IBlockCipher
+ {
+ private const int BLOCK_SIZE = 8;
+ private int[] workingKey;
+ /**
+ * standard constructor.
+ */
+ public IdeaEngine()
+ {
+ }
+ /**
+ * initialise an IDEA cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("invalid parameter passed to IDEA init - " + parameters.GetType().ToString());
+
+ workingKey = GenerateWorkingKey(forEncryption,
+ ((KeyParameter)parameters).GetKey());
+ }
+
+ public string AlgorithmName
+ {
+ get { return "IDEA"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new InvalidOperationException("IDEA engine not initialised");
+ }
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ IdeaFunc(workingKey, input, inOff, output, outOff);
+ return BLOCK_SIZE;
+ }
+ public void Reset()
+ {
+ }
+ private static readonly int MASK = 0xffff;
+ private static readonly int BASE = 0x10001;
+ private int BytesToWord(
+ byte[] input,
+ int inOff)
+ {
+ return ((input[inOff] << 8) & 0xff00) + (input[inOff + 1] & 0xff);
+ }
+ private void WordToBytes(
+ int word,
+ byte[] outBytes,
+ int outOff)
+ {
+ outBytes[outOff] = (byte)((uint) word >> 8);
+ outBytes[outOff + 1] = (byte)word;
+ }
+ /**
+ * return x = x * y where the multiplication is done modulo
+ * 65537 (0x10001) (as defined in the IDEA specification) and
+ * a zero input is taken to be 65536 (0x10000).
+ *
+ * @param x the x value
+ * @param y the y value
+ * @return x = x * y
+ */
+ private int Mul(
+ int x,
+ int y)
+ {
+ if (x == 0)
+ {
+ x = (BASE - y);
+ }
+ else if (y == 0)
+ {
+ x = (BASE - x);
+ }
+ else
+ {
+ int p = x * y;
+ y = p & MASK;
+ x = (int) ((uint) p >> 16);
+ x = y - x + ((y < x) ? 1 : 0);
+ }
+ return x & MASK;
+ }
+ private void IdeaFunc(
+ int[] workingKey,
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ int x0, x1, x2, x3, t0, t1;
+ int keyOff = 0;
+ x0 = BytesToWord(input, inOff);
+ x1 = BytesToWord(input, inOff + 2);
+ x2 = BytesToWord(input, inOff + 4);
+ x3 = BytesToWord(input, inOff + 6);
+ for (int round = 0; round < 8; round++)
+ {
+ x0 = Mul(x0, workingKey[keyOff++]);
+ x1 += workingKey[keyOff++];
+ x1 &= MASK;
+ x2 += workingKey[keyOff++];
+ x2 &= MASK;
+ x3 = Mul(x3, workingKey[keyOff++]);
+ t0 = x1;
+ t1 = x2;
+ x2 ^= x0;
+ x1 ^= x3;
+ x2 = Mul(x2, workingKey[keyOff++]);
+ x1 += x2;
+ x1 &= MASK;
+ x1 = Mul(x1, workingKey[keyOff++]);
+ x2 += x1;
+ x2 &= MASK;
+ x0 ^= x1;
+ x3 ^= x2;
+ x1 ^= t1;
+ x2 ^= t0;
+ }
+ WordToBytes(Mul(x0, workingKey[keyOff++]), outBytes, outOff);
+ WordToBytes(x2 + workingKey[keyOff++], outBytes, outOff + 2); /* NB: Order */
+ WordToBytes(x1 + workingKey[keyOff++], outBytes, outOff + 4);
+ WordToBytes(Mul(x3, workingKey[keyOff]), outBytes, outOff + 6);
+ }
+ /**
+ * The following function is used to expand the user key to the encryption
+ * subkey. The first 16 bytes are the user key, and the rest of the subkey
+ * is calculated by rotating the previous 16 bytes by 25 bits to the left,
+ * and so on until the subkey is completed.
+ */
+ private int[] ExpandKey(
+ byte[] uKey)
+ {
+ int[] key = new int[52];
+ if (uKey.Length < 16)
+ {
+ byte[] tmp = new byte[16];
+ Array.Copy(uKey, 0, tmp, tmp.Length - uKey.Length, uKey.Length);
+ uKey = tmp;
+ }
+ for (int i = 0; i < 8; i++)
+ {
+ key[i] = BytesToWord(uKey, i * 2);
+ }
+ for (int i = 8; i < 52; i++)
+ {
+ if ((i & 7) < 6)
+ {
+ key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7) & MASK;
+ }
+ else if ((i & 7) == 6)
+ {
+ key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7) & MASK;
+ }
+ else
+ {
+ key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7) & MASK;
+ }
+ }
+ return key;
+ }
+ /**
+ * This function computes multiplicative inverse using Euclid's Greatest
+ * Common Divisor algorithm. Zero and one are self inverse.
+ * <p>
+ * i.e. x * MulInv(x) == 1 (modulo BASE)
+ * </p>
+ */
+ private int MulInv(
+ int x)
+ {
+ int t0, t1, q, y;
+
+ if (x < 2)
+ {
+ return x;
+ }
+ t0 = 1;
+ t1 = BASE / x;
+ y = BASE % x;
+ while (y != 1)
+ {
+ q = x / y;
+ x = x % y;
+ t0 = (t0 + (t1 * q)) & MASK;
+ if (x == 1)
+ {
+ return t0;
+ }
+ q = y / x;
+ y = y % x;
+ t1 = (t1 + (t0 * q)) & MASK;
+ }
+ return (1 - t1) & MASK;
+ }
+ /**
+ * Return the additive inverse of x.
+ * <p>
+ * i.e. x + AddInv(x) == 0
+ * </p>
+ */
+ int AddInv(
+ int x)
+ {
+ return (0 - x) & MASK;
+ }
+
+ /**
+ * The function to invert the encryption subkey to the decryption subkey.
+ * It also involves the multiplicative inverse and the additive inverse functions.
+ */
+ private int[] InvertKey(
+ int[] inKey)
+ {
+ int t1, t2, t3, t4;
+ int p = 52; /* We work backwards */
+ int[] key = new int[52];
+ int inOff = 0;
+
+ t1 = MulInv(inKey[inOff++]);
+ t2 = AddInv(inKey[inOff++]);
+ t3 = AddInv(inKey[inOff++]);
+ t4 = MulInv(inKey[inOff++]);
+ key[--p] = t4;
+ key[--p] = t3;
+ key[--p] = t2;
+ key[--p] = t1;
+
+ for (int round = 1; round < 8; round++)
+ {
+ t1 = inKey[inOff++];
+ t2 = inKey[inOff++];
+ key[--p] = t2;
+ key[--p] = t1;
+
+ t1 = MulInv(inKey[inOff++]);
+ t2 = AddInv(inKey[inOff++]);
+ t3 = AddInv(inKey[inOff++]);
+ t4 = MulInv(inKey[inOff++]);
+ key[--p] = t4;
+ key[--p] = t2; /* NB: Order */
+ key[--p] = t3;
+ key[--p] = t1;
+ }
+ t1 = inKey[inOff++];
+ t2 = inKey[inOff++];
+ key[--p] = t2;
+ key[--p] = t1;
+
+ t1 = MulInv(inKey[inOff++]);
+ t2 = AddInv(inKey[inOff++]);
+ t3 = AddInv(inKey[inOff++]);
+ t4 = MulInv(inKey[inOff]);
+ key[--p] = t4;
+ key[--p] = t3;
+ key[--p] = t2;
+ key[--p] = t1;
+ return key;
+ }
+
+ private int[] GenerateWorkingKey(
+ bool forEncryption,
+ byte[] userKey)
+ {
+ if (forEncryption)
+ {
+ return ExpandKey(userKey);
+ }
+ else
+ {
+ return InvertKey(ExpandKey(userKey));
+ }
+ }
+ }
+}
+
+#endif
diff --git a/Crypto/src/crypto/engines/IesEngine.cs b/Crypto/src/crypto/engines/IesEngine.cs
new file mode 100644
index 000000000..c49b2a9ee
--- /dev/null
+++ b/Crypto/src/crypto/engines/IesEngine.cs
@@ -0,0 +1,236 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * support class for constructing intergrated encryption ciphers
+ * for doing basic message exchanges on top of key agreement ciphers
+ */
+ public class IesEngine
+ {
+ private readonly IBasicAgreement agree;
+ private readonly IDerivationFunction kdf;
+ private readonly IMac mac;
+ private readonly BufferedBlockCipher cipher;
+ private readonly byte[] macBuf;
+
+ private bool forEncryption;
+ private ICipherParameters privParam, pubParam;
+ private IesParameters param;
+
+ /**
+ * set up for use with stream mode, where the key derivation function
+ * is used to provide a stream of bytes to xor with the message.
+ *
+ * @param agree the key agreement used as the basis for the encryption
+ * @param kdf the key derivation function used for byte generation
+ * @param mac the message authentication code generator for the message
+ */
+ public IesEngine(
+ IBasicAgreement agree,
+ IDerivationFunction kdf,
+ IMac mac)
+ {
+ this.agree = agree;
+ this.kdf = kdf;
+ this.mac = mac;
+ this.macBuf = new byte[mac.GetMacSize()];
+// this.cipher = null;
+ }
+
+ /**
+ * set up for use in conjunction with a block cipher to handle the
+ * message.
+ *
+ * @param agree the key agreement used as the basis for the encryption
+ * @param kdf the key derivation function used for byte generation
+ * @param mac the message authentication code generator for the message
+ * @param cipher the cipher to used for encrypting the message
+ */
+ public IesEngine(
+ IBasicAgreement agree,
+ IDerivationFunction kdf,
+ IMac mac,
+ BufferedBlockCipher cipher)
+ {
+ this.agree = agree;
+ this.kdf = kdf;
+ this.mac = mac;
+ this.macBuf = new byte[mac.GetMacSize()];
+ this.cipher = cipher;
+ }
+
+ /**
+ * Initialise the encryptor.
+ *
+ * @param forEncryption whether or not this is encryption/decryption.
+ * @param privParam our private key parameters
+ * @param pubParam the recipient's/sender's public key parameters
+ * @param param encoding and derivation parameters.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters privParameters,
+ ICipherParameters pubParameters,
+ ICipherParameters iesParameters)
+ {
+ this.forEncryption = forEncryption;
+ this.privParam = privParameters;
+ this.pubParam = pubParameters;
+ this.param = (IesParameters)iesParameters;
+ }
+
+ private byte[] DecryptBlock(
+ byte[] in_enc,
+ int inOff,
+ int inLen,
+ byte[] z)
+ {
+ byte[] M = null;
+ KeyParameter macKey = null;
+ KdfParameters kParam = new KdfParameters(z, param.GetDerivationV());
+ int macKeySize = param.MacKeySize;
+
+ kdf.Init(kParam);
+
+ inLen -= mac.GetMacSize();
+
+ if (cipher == null) // stream mode
+ {
+ byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8));
+
+ M = new byte[inLen];
+
+ for (int i = 0; i != inLen; i++)
+ {
+ M[i] = (byte)(in_enc[inOff + i] ^ Buffer[i]);
+ }
+
+ macKey = new KeyParameter(Buffer, inLen, (macKeySize / 8));
+ }
+ else
+ {
+ int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize;
+ byte[] Buffer = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8));
+
+ cipher.Init(false, new KeyParameter(Buffer, 0, (cipherKeySize / 8)));
+
+ M = cipher.DoFinal(in_enc, inOff, inLen);
+
+ macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8));
+ }
+
+ byte[] macIV = param.GetEncodingV();
+
+ mac.Init(macKey);
+ mac.BlockUpdate(in_enc, inOff, inLen);
+ mac.BlockUpdate(macIV, 0, macIV.Length);
+ mac.DoFinal(macBuf, 0);
+
+ inOff += inLen;
+
+ for (int t = 0; t < macBuf.Length; t++)
+ {
+ if (macBuf[t] != in_enc[inOff + t])
+ {
+ throw (new InvalidCipherTextException("IMac codes failed to equal."));
+ }
+ }
+
+ return M;
+ }
+
+ private byte[] EncryptBlock(
+ byte[] input,
+ int inOff,
+ int inLen,
+ byte[] z)
+ {
+ byte[] C = null;
+ KeyParameter macKey = null;
+ KdfParameters kParam = new KdfParameters(z, param.GetDerivationV());
+ int c_text_length = 0;
+ int macKeySize = param.MacKeySize;
+
+ if (cipher == null) // stream mode
+ {
+ byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8));
+
+ C = new byte[inLen + mac.GetMacSize()];
+ c_text_length = inLen;
+
+ for (int i = 0; i != inLen; i++)
+ {
+ C[i] = (byte)(input[inOff + i] ^ Buffer[i]);
+ }
+
+ macKey = new KeyParameter(Buffer, inLen, (macKeySize / 8));
+ }
+ else
+ {
+ int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize;
+ byte[] Buffer = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8));
+
+ cipher.Init(true, new KeyParameter(Buffer, 0, (cipherKeySize / 8)));
+
+ c_text_length = cipher.GetOutputSize(inLen);
+ byte[] tmp = new byte[c_text_length];
+
+ int len = cipher.ProcessBytes(input, inOff, inLen, tmp, 0);
+ len += cipher.DoFinal(tmp, len);
+
+ C = new byte[len + mac.GetMacSize()];
+ c_text_length = len;
+
+ Array.Copy(tmp, 0, C, 0, len);
+
+ macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8));
+ }
+
+ byte[] macIV = param.GetEncodingV();
+
+ mac.Init(macKey);
+ mac.BlockUpdate(C, 0, c_text_length);
+ mac.BlockUpdate(macIV, 0, macIV.Length);
+ //
+ // return the message and it's MAC
+ //
+ mac.DoFinal(C, c_text_length);
+ return C;
+ }
+
+ private byte[] GenerateKdfBytes(
+ KdfParameters kParam,
+ int length)
+ {
+ byte[] buf = new byte[length];
+
+ kdf.Init(kParam);
+
+ kdf.GenerateBytes(buf, 0, buf.Length);
+
+ return buf;
+ }
+
+ public byte[] ProcessBlock(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ agree.Init(privParam);
+
+ BigInteger z = agree.CalculateAgreement(pubParam);
+
+ // TODO Is a fixed length result expected?
+ byte[] zBytes = z.ToByteArrayUnsigned();
+
+ return forEncryption
+ ? EncryptBlock(input, inOff, inLen, zBytes)
+ : DecryptBlock(input, inOff, inLen, zBytes);
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/NaccacheSternEngine.cs b/Crypto/src/crypto/engines/NaccacheSternEngine.cs
new file mode 100644
index 000000000..9a0d1e0fe
--- /dev/null
+++ b/Crypto/src/crypto/engines/NaccacheSternEngine.cs
@@ -0,0 +1,432 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * NaccacheStern Engine. For details on this cipher, please see
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+ public class NaccacheSternEngine
+ : IAsymmetricBlockCipher
+ {
+ private bool forEncryption;
+
+ private NaccacheSternKeyParameters key;
+
+ private IList[] lookup = null;
+
+ private bool debug = false;
+
+ public string AlgorithmName
+ {
+ get { return "NaccacheStern"; }
+ }
+
+ /**
+ * Initializes this algorithm. Must be called before all other Functions.
+ *
+ * @see org.bouncycastle.crypto.AsymmetricBlockCipher#init(bool,
+ * org.bouncycastle.crypto.CipherParameters)
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom) parameters).Parameters;
+ }
+
+ key = (NaccacheSternKeyParameters)parameters;
+
+ // construct lookup table for faster decryption if necessary
+ if (!this.forEncryption)
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("Constructing lookup Array");
+ }
+ NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key;
+ IList primes = priv.SmallPrimesList;
+ lookup = new IList[primes.Count];
+ for (int i = 0; i < primes.Count; i++)
+ {
+ BigInteger actualPrime = (BigInteger) primes[i];
+ int actualPrimeValue = actualPrime.IntValue;
+
+ lookup[i] = Platform.CreateArrayList(actualPrimeValue);
+ lookup[i].Add(BigInteger.One);
+
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("Constructing lookup ArrayList for " + actualPrimeValue);
+ }
+
+ BigInteger accJ = BigInteger.Zero;
+
+ for (int j = 1; j < actualPrimeValue; j++)
+ {
+// BigInteger bigJ = BigInteger.ValueOf(j);
+// accJ = priv.PhiN.Multiply(bigJ);
+ accJ = accJ.Add(priv.PhiN);
+ BigInteger comp = accJ.Divide(actualPrime);
+ lookup[i].Add(priv.G.ModPow(comp, priv.Modulus));
+ }
+ }
+ }
+ }
+
+ public bool Debug
+ {
+ set { this.debug = value; }
+ }
+
+ /**
+ * Returns the input block size of this algorithm.
+ *
+ * @see org.bouncycastle.crypto.AsymmetricBlockCipher#GetInputBlockSize()
+ */
+ public int GetInputBlockSize()
+ {
+ if (forEncryption)
+ {
+ // We can only encrypt values up to lowerSigmaBound
+ return (key.LowerSigmaBound + 7) / 8 - 1;
+ }
+ else
+ {
+ // We pad to modulus-size bytes for easier decryption.
+// return key.Modulus.ToByteArray().Length;
+ return key.Modulus.BitLength / 8 + 1;
+ }
+ }
+
+ /**
+ * Returns the output block size of this algorithm.
+ *
+ * @see org.bouncycastle.crypto.AsymmetricBlockCipher#GetOutputBlockSize()
+ */
+ public int GetOutputBlockSize()
+ {
+ if (forEncryption)
+ {
+ // encrypted Data is always padded up to modulus size
+// return key.Modulus.ToByteArray().Length;
+ return key.Modulus.BitLength / 8 + 1;
+ }
+ else
+ {
+ // decrypted Data has upper limit lowerSigmaBound
+ return (key.LowerSigmaBound + 7) / 8 - 1;
+ }
+ }
+
+ /**
+ * Process a single Block using the Naccache-Stern algorithm.
+ *
+ * @see org.bouncycastle.crypto.AsymmetricBlockCipher#ProcessBlock(byte[],
+ * int, int)
+ */
+ public byte[] ProcessBlock(
+ byte[] inBytes,
+ int inOff,
+ int length)
+ {
+ if (key == null)
+ throw new InvalidOperationException("NaccacheStern engine not initialised");
+ if (length > (GetInputBlockSize() + 1))
+ throw new DataLengthException("input too large for Naccache-Stern cipher.\n");
+
+ if (!forEncryption)
+ {
+ // At decryption make sure that we receive padded data blocks
+ if (length < GetInputBlockSize())
+ {
+ throw new InvalidCipherTextException("BlockLength does not match modulus for Naccache-Stern cipher.\n");
+ }
+ }
+
+ // transform input into BigInteger
+ BigInteger input = new BigInteger(1, inBytes, inOff, length);
+
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("input as BigInteger: " + input);
+ }
+
+ byte[] output;
+ if (forEncryption)
+ {
+ output = Encrypt(input);
+ }
+ else
+ {
+ IList plain = Platform.CreateArrayList();
+ NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key;
+ IList primes = priv.SmallPrimesList;
+ // Get Chinese Remainders of CipherText
+ for (int i = 0; i < primes.Count; i++)
+ {
+ BigInteger exp = input.ModPow(priv.PhiN.Divide((BigInteger)primes[i]), priv.Modulus);
+ IList al = lookup[i];
+ if (lookup[i].Count != ((BigInteger)primes[i]).IntValue)
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("Prime is " + primes[i] + ", lookup table has size " + al.Count);
+ }
+ throw new InvalidCipherTextException("Error in lookup Array for "
+ + ((BigInteger)primes[i]).IntValue
+ + ": Size mismatch. Expected ArrayList with length "
+ + ((BigInteger)primes[i]).IntValue + " but found ArrayList of length "
+ + lookup[i].Count);
+ }
+ int lookedup = al.IndexOf(exp);
+
+ if (lookedup == -1)
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("Actual prime is " + primes[i]);
+ System.Diagnostics.Debug.WriteLine("Decrypted value is " + exp);
+
+ System.Diagnostics.Debug.WriteLine("LookupList for " + primes[i] + " with size " + lookup[i].Count
+ + " is: ");
+ for (int j = 0; j < lookup[i].Count; j++)
+ {
+ System.Diagnostics.Debug.WriteLine(lookup[i][j]);
+ }
+ }
+ throw new InvalidCipherTextException("Lookup failed");
+ }
+ plain.Add(BigInteger.ValueOf(lookedup));
+ }
+ BigInteger test = chineseRemainder(plain, primes);
+
+ // Should not be used as an oracle, so reencrypt output to see
+ // if it corresponds to input
+
+ // this breaks probabilisic encryption, so disable it. Anyway, we do
+ // use the first n primes for key generation, so it is pretty easy
+ // to guess them. But as stated in the paper, this is not a security
+ // breach. So we can just work with the correct sigma.
+
+ // if (debug) {
+ // Console.WriteLine("Decryption is " + test);
+ // }
+ // if ((key.G.ModPow(test, key.Modulus)).Equals(input)) {
+ // output = test.ToByteArray();
+ // } else {
+ // if(debug){
+ // Console.WriteLine("Engine seems to be used as an oracle,
+ // returning null");
+ // }
+ // output = null;
+ // }
+
+ output = test.ToByteArray();
+ }
+
+ return output;
+ }
+
+ /**
+ * Encrypts a BigInteger aka Plaintext with the public key.
+ *
+ * @param plain
+ * The BigInteger to encrypt
+ * @return The byte[] representation of the encrypted BigInteger (i.e.
+ * crypted.toByteArray())
+ */
+ public byte[] Encrypt(
+ BigInteger plain)
+ {
+ // Always return modulus size values 0-padded at the beginning
+ // 0-padding at the beginning is correctly parsed by BigInteger :)
+// byte[] output = key.Modulus.ToByteArray();
+// Array.Clear(output, 0, output.Length);
+ byte[] output = new byte[key.Modulus.BitLength / 8 + 1];
+
+ byte[] tmp = key.G.ModPow(plain, key.Modulus).ToByteArray();
+ Array.Copy(tmp, 0, output, output.Length - tmp.Length, tmp.Length);
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("Encrypted value is: " + new BigInteger(output));
+ }
+ return output;
+ }
+
+ /**
+ * Adds the contents of two encrypted blocks mod sigma
+ *
+ * @param block1
+ * the first encrypted block
+ * @param block2
+ * the second encrypted block
+ * @return encrypt((block1 + block2) mod sigma)
+ * @throws InvalidCipherTextException
+ */
+ public byte[] AddCryptedBlocks(
+ byte[] block1,
+ byte[] block2)
+ {
+ // check for correct blocksize
+ if (forEncryption)
+ {
+ if ((block1.Length > GetOutputBlockSize())
+ || (block2.Length > GetOutputBlockSize()))
+ {
+ throw new InvalidCipherTextException(
+ "BlockLength too large for simple addition.\n");
+ }
+ }
+ else
+ {
+ if ((block1.Length > GetInputBlockSize())
+ || (block2.Length > GetInputBlockSize()))
+ {
+ throw new InvalidCipherTextException(
+ "BlockLength too large for simple addition.\n");
+ }
+ }
+
+ // calculate resulting block
+ BigInteger m1Crypt = new BigInteger(1, block1);
+ BigInteger m2Crypt = new BigInteger(1, block2);
+ BigInteger m1m2Crypt = m1Crypt.Multiply(m2Crypt);
+ m1m2Crypt = m1m2Crypt.Mod(key.Modulus);
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("c(m1) as BigInteger:....... " + m1Crypt);
+ System.Diagnostics.Debug.WriteLine("c(m2) as BigInteger:....... " + m2Crypt);
+ System.Diagnostics.Debug.WriteLine("c(m1)*c(m2)%n = c(m1+m2)%n: " + m1m2Crypt);
+ }
+
+ //byte[] output = key.Modulus.ToByteArray();
+ //Array.Clear(output, 0, output.Length);
+ byte[] output = new byte[key.Modulus.BitLength / 8 + 1];
+
+ byte[] m1m2CryptBytes = m1m2Crypt.ToByteArray();
+ Array.Copy(m1m2CryptBytes, 0, output,
+ output.Length - m1m2CryptBytes.Length, m1m2CryptBytes.Length);
+
+ return output;
+ }
+
+ /**
+ * Convenience Method for data exchange with the cipher.
+ *
+ * Determines blocksize and splits data to blocksize.
+ *
+ * @param data the data to be processed
+ * @return the data after it went through the NaccacheSternEngine.
+ * @throws InvalidCipherTextException
+ */
+ public byte[] ProcessData(
+ byte[] data)
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("");
+ }
+ if (data.Length > GetInputBlockSize())
+ {
+ int inBlocksize = GetInputBlockSize();
+ int outBlocksize = GetOutputBlockSize();
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("Input blocksize is: " + inBlocksize + " bytes");
+ System.Diagnostics.Debug.WriteLine("Output blocksize is: " + outBlocksize + " bytes");
+ System.Diagnostics.Debug.WriteLine("Data has length:.... " + data.Length + " bytes");
+ }
+ int datapos = 0;
+ int retpos = 0;
+ byte[] retval = new byte[(data.Length / inBlocksize + 1) * outBlocksize];
+ while (datapos < data.Length)
+ {
+ byte[] tmp;
+ if (datapos + inBlocksize < data.Length)
+ {
+ tmp = ProcessBlock(data, datapos, inBlocksize);
+ datapos += inBlocksize;
+ }
+ else
+ {
+ tmp = ProcessBlock(data, datapos, data.Length - datapos);
+ datapos += data.Length - datapos;
+ }
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("new datapos is " + datapos);
+ }
+ if (tmp != null)
+ {
+ tmp.CopyTo(retval, retpos);
+ retpos += tmp.Length;
+ }
+ else
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("cipher returned null");
+ }
+ throw new InvalidCipherTextException("cipher returned null");
+ }
+ }
+ byte[] ret = new byte[retpos];
+ Array.Copy(retval, 0, ret, 0, retpos);
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("returning " + ret.Length + " bytes");
+ }
+ return ret;
+ }
+ else
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("data size is less then input block size, processing directly");
+ }
+ return ProcessBlock(data, 0, data.Length);
+ }
+ }
+
+ /**
+ * Computes the integer x that is expressed through the given primes and the
+ * congruences with the chinese remainder theorem (CRT).
+ *
+ * @param congruences
+ * the congruences c_i
+ * @param primes
+ * the primes p_i
+ * @return an integer x for that x % p_i == c_i
+ */
+ private static BigInteger chineseRemainder(IList congruences, IList primes)
+ {
+ BigInteger retval = BigInteger.Zero;
+ BigInteger all = BigInteger.One;
+ for (int i = 0; i < primes.Count; i++)
+ {
+ all = all.Multiply((BigInteger)primes[i]);
+ }
+ for (int i = 0; i < primes.Count; i++)
+ {
+ BigInteger a = (BigInteger)primes[i];
+ BigInteger b = all.Divide(a);
+ BigInteger b2 = b.ModInverse(a);
+ BigInteger tmp = b.Multiply(b2);
+ tmp = tmp.Multiply((BigInteger)congruences[i]);
+ retval = retval.Add(tmp);
+ }
+
+ return retval.Mod(all);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/NoekeonEngine.cs b/Crypto/src/crypto/engines/NoekeonEngine.cs
new file mode 100644
index 000000000..b73e696a9
--- /dev/null
+++ b/Crypto/src/crypto/engines/NoekeonEngine.cs
@@ -0,0 +1,240 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * A Noekeon engine, using direct-key mode.
+ */
+ public class NoekeonEngine
+ : IBlockCipher
+ {
+ private const int GenericSize = 16; // Block and key size, as well as the amount of rounds.
+
+ private static readonly uint[] nullVector =
+ {
+ 0x00, 0x00, 0x00, 0x00 // Used in decryption
+ };
+
+ private static readonly uint[] roundConstants =
+ {
+ 0x80, 0x1b, 0x36, 0x6c,
+ 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63,
+ 0xc6, 0x97, 0x35, 0x6a,
+ 0xd4
+ };
+
+ private uint[] state = new uint[4], // a
+ subKeys = new uint[4], // k
+ decryptKeys = new uint[4];
+
+ private bool _initialised, _forEncryption;
+
+ /**
+ * Create an instance of the Noekeon encryption algorithm
+ * and set some defaults
+ */
+ public NoekeonEngine()
+ {
+ _initialised = false;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Noekeon"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return GenericSize;
+ }
+
+ /**
+ * initialise
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception ArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("Invalid parameters passed to Noekeon init - " + parameters.GetType().Name, "parameters");
+
+ _forEncryption = forEncryption;
+ _initialised = true;
+
+ KeyParameter p = (KeyParameter) parameters;
+
+ setKey(p.GetKey());
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (!_initialised)
+ throw new InvalidOperationException(AlgorithmName + " not initialised");
+ if ((inOff + GenericSize) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + GenericSize) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ return _forEncryption
+ ? encryptBlock(input, inOff, output, outOff)
+ : decryptBlock(input, inOff, output, outOff);
+ }
+
+ public void Reset()
+ {
+ // TODO This should do something in case the encryption is aborted
+ }
+
+ /**
+ * Re-key the cipher.
+ *
+ * @param key the key to be used
+ */
+ private void setKey(byte[] key)
+ {
+ subKeys[0] = Pack.BE_To_UInt32(key, 0);
+ subKeys[1] = Pack.BE_To_UInt32(key, 4);
+ subKeys[2] = Pack.BE_To_UInt32(key, 8);
+ subKeys[3] = Pack.BE_To_UInt32(key, 12);
+ }
+
+ private int encryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ state[0] = Pack.BE_To_UInt32(input, inOff);
+ state[1] = Pack.BE_To_UInt32(input, inOff+4);
+ state[2] = Pack.BE_To_UInt32(input, inOff+8);
+ state[3] = Pack.BE_To_UInt32(input, inOff+12);
+
+ int i;
+ for (i = 0; i < GenericSize; i++)
+ {
+ state[0] ^= roundConstants[i];
+ theta(state, subKeys);
+ pi1(state);
+ gamma(state);
+ pi2(state);
+ }
+
+ state[0] ^= roundConstants[i];
+ theta(state, subKeys);
+
+ Pack.UInt32_To_BE(state[0], output, outOff);
+ Pack.UInt32_To_BE(state[1], output, outOff+4);
+ Pack.UInt32_To_BE(state[2], output, outOff+8);
+ Pack.UInt32_To_BE(state[3], output, outOff+12);
+
+ return GenericSize;
+ }
+
+ private int decryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ state[0] = Pack.BE_To_UInt32(input, inOff);
+ state[1] = Pack.BE_To_UInt32(input, inOff+4);
+ state[2] = Pack.BE_To_UInt32(input, inOff+8);
+ state[3] = Pack.BE_To_UInt32(input, inOff+12);
+
+ Array.Copy(subKeys, 0, decryptKeys, 0, subKeys.Length);
+ theta(decryptKeys, nullVector);
+
+ int i;
+ for (i = GenericSize; i > 0; i--)
+ {
+ theta(state, decryptKeys);
+ state[0] ^= roundConstants[i];
+ pi1(state);
+ gamma(state);
+ pi2(state);
+ }
+
+ theta(state, decryptKeys);
+ state[0] ^= roundConstants[i];
+
+ Pack.UInt32_To_BE(state[0], output, outOff);
+ Pack.UInt32_To_BE(state[1], output, outOff+4);
+ Pack.UInt32_To_BE(state[2], output, outOff+8);
+ Pack.UInt32_To_BE(state[3], output, outOff+12);
+
+ return GenericSize;
+ }
+
+ private void gamma(uint[] a)
+ {
+ a[1] ^= ~a[3] & ~a[2];
+ a[0] ^= a[2] & a[1];
+
+ uint tmp = a[3];
+ a[3] = a[0];
+ a[0] = tmp;
+ a[2] ^= a[0]^a[1]^a[3];
+
+ a[1] ^= ~a[3] & ~a[2];
+ a[0] ^= a[2] & a[1];
+ }
+
+ private void theta(uint[] a, uint[] k)
+ {
+ uint tmp;
+ tmp = a[0]^a[2];
+ tmp ^= rotl(tmp,8)^rotl(tmp,24);
+ a[1] ^= tmp;
+ a[3] ^= tmp;
+
+ for (int i = 0; i < 4; i++)
+ {
+ a[i] ^= k[i];
+ }
+
+ tmp = a[1]^a[3];
+ tmp ^= rotl(tmp,8)^rotl(tmp,24);
+ a[0] ^= tmp;
+ a[2] ^= tmp;
+ }
+
+ private void pi1(uint[] a)
+ {
+ a[1] = rotl(a[1], 1);
+ a[2] = rotl(a[2], 5);
+ a[3] = rotl(a[3], 2);
+ }
+
+ private void pi2(uint[] a)
+ {
+ a[1] = rotl(a[1], 31);
+ a[2] = rotl(a[2], 27);
+ a[3] = rotl(a[3], 30);
+ }
+
+ // Helpers
+
+ private uint rotl(uint x, int y)
+ {
+ return (x << y) | (x >> (32-y));
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/NullEngine.cs b/Crypto/src/crypto/engines/NullEngine.cs
new file mode 100644
index 000000000..407b8ccc6
--- /dev/null
+++ b/Crypto/src/crypto/engines/NullEngine.cs
@@ -0,0 +1,70 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * The no-op engine that just copies bytes through, irrespective of whether encrypting and decrypting.
+ * Provided for the sake of completeness.
+ */
+ public class NullEngine
+ : IBlockCipher
+ {
+ private bool initialised;
+ private const int BlockSize = 1;
+
+ public NullEngine()
+ {
+ }
+
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ // we don't mind any parameters that may come in
+ initialised = true;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Null"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return true; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BlockSize;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (!initialised)
+ throw new InvalidOperationException("Null engine not initialised");
+ if ((inOff + BlockSize) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + BlockSize) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ for (int i = 0; i < BlockSize; ++i)
+ {
+ output[outOff + i] = input[inOff + i];
+ }
+
+ return BlockSize;
+ }
+
+ public void Reset()
+ {
+ // nothing needs to be done
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/RC2Engine.cs b/Crypto/src/crypto/engines/RC2Engine.cs
new file mode 100644
index 000000000..aaf8c714c
--- /dev/null
+++ b/Crypto/src/crypto/engines/RC2Engine.cs
@@ -0,0 +1,312 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * an implementation of RC2 as described in RFC 2268
+ * "A Description of the RC2(r) Encryption Algorithm" R. Rivest.
+ */
+ public class RC2Engine
+ : IBlockCipher
+ {
+ //
+ // the values we use for key expansion (based on the digits of PI)
+ //
+ private static readonly byte[] piTable =
+ {
+ (byte)0xd9, (byte)0x78, (byte)0xf9, (byte)0xc4, (byte)0x19, (byte)0xdd, (byte)0xb5, (byte)0xed,
+ (byte)0x28, (byte)0xe9, (byte)0xfd, (byte)0x79, (byte)0x4a, (byte)0xa0, (byte)0xd8, (byte)0x9d,
+ (byte)0xc6, (byte)0x7e, (byte)0x37, (byte)0x83, (byte)0x2b, (byte)0x76, (byte)0x53, (byte)0x8e,
+ (byte)0x62, (byte)0x4c, (byte)0x64, (byte)0x88, (byte)0x44, (byte)0x8b, (byte)0xfb, (byte)0xa2,
+ (byte)0x17, (byte)0x9a, (byte)0x59, (byte)0xf5, (byte)0x87, (byte)0xb3, (byte)0x4f, (byte)0x13,
+ (byte)0x61, (byte)0x45, (byte)0x6d, (byte)0x8d, (byte)0x9, (byte)0x81, (byte)0x7d, (byte)0x32,
+ (byte)0xbd, (byte)0x8f, (byte)0x40, (byte)0xeb, (byte)0x86, (byte)0xb7, (byte)0x7b, (byte)0xb,
+ (byte)0xf0, (byte)0x95, (byte)0x21, (byte)0x22, (byte)0x5c, (byte)0x6b, (byte)0x4e, (byte)0x82,
+ (byte)0x54, (byte)0xd6, (byte)0x65, (byte)0x93, (byte)0xce, (byte)0x60, (byte)0xb2, (byte)0x1c,
+ (byte)0x73, (byte)0x56, (byte)0xc0, (byte)0x14, (byte)0xa7, (byte)0x8c, (byte)0xf1, (byte)0xdc,
+ (byte)0x12, (byte)0x75, (byte)0xca, (byte)0x1f, (byte)0x3b, (byte)0xbe, (byte)0xe4, (byte)0xd1,
+ (byte)0x42, (byte)0x3d, (byte)0xd4, (byte)0x30, (byte)0xa3, (byte)0x3c, (byte)0xb6, (byte)0x26,
+ (byte)0x6f, (byte)0xbf, (byte)0xe, (byte)0xda, (byte)0x46, (byte)0x69, (byte)0x7, (byte)0x57,
+ (byte)0x27, (byte)0xf2, (byte)0x1d, (byte)0x9b, (byte)0xbc, (byte)0x94, (byte)0x43, (byte)0x3,
+ (byte)0xf8, (byte)0x11, (byte)0xc7, (byte)0xf6, (byte)0x90, (byte)0xef, (byte)0x3e, (byte)0xe7,
+ (byte)0x6, (byte)0xc3, (byte)0xd5, (byte)0x2f, (byte)0xc8, (byte)0x66, (byte)0x1e, (byte)0xd7,
+ (byte)0x8, (byte)0xe8, (byte)0xea, (byte)0xde, (byte)0x80, (byte)0x52, (byte)0xee, (byte)0xf7,
+ (byte)0x84, (byte)0xaa, (byte)0x72, (byte)0xac, (byte)0x35, (byte)0x4d, (byte)0x6a, (byte)0x2a,
+ (byte)0x96, (byte)0x1a, (byte)0xd2, (byte)0x71, (byte)0x5a, (byte)0x15, (byte)0x49, (byte)0x74,
+ (byte)0x4b, (byte)0x9f, (byte)0xd0, (byte)0x5e, (byte)0x4, (byte)0x18, (byte)0xa4, (byte)0xec,
+ (byte)0xc2, (byte)0xe0, (byte)0x41, (byte)0x6e, (byte)0xf, (byte)0x51, (byte)0xcb, (byte)0xcc,
+ (byte)0x24, (byte)0x91, (byte)0xaf, (byte)0x50, (byte)0xa1, (byte)0xf4, (byte)0x70, (byte)0x39,
+ (byte)0x99, (byte)0x7c, (byte)0x3a, (byte)0x85, (byte)0x23, (byte)0xb8, (byte)0xb4, (byte)0x7a,
+ (byte)0xfc, (byte)0x2, (byte)0x36, (byte)0x5b, (byte)0x25, (byte)0x55, (byte)0x97, (byte)0x31,
+ (byte)0x2d, (byte)0x5d, (byte)0xfa, (byte)0x98, (byte)0xe3, (byte)0x8a, (byte)0x92, (byte)0xae,
+ (byte)0x5, (byte)0xdf, (byte)0x29, (byte)0x10, (byte)0x67, (byte)0x6c, (byte)0xba, (byte)0xc9,
+ (byte)0xd3, (byte)0x0, (byte)0xe6, (byte)0xcf, (byte)0xe1, (byte)0x9e, (byte)0xa8, (byte)0x2c,
+ (byte)0x63, (byte)0x16, (byte)0x1, (byte)0x3f, (byte)0x58, (byte)0xe2, (byte)0x89, (byte)0xa9,
+ (byte)0xd, (byte)0x38, (byte)0x34, (byte)0x1b, (byte)0xab, (byte)0x33, (byte)0xff, (byte)0xb0,
+ (byte)0xbb, (byte)0x48, (byte)0xc, (byte)0x5f, (byte)0xb9, (byte)0xb1, (byte)0xcd, (byte)0x2e,
+ (byte)0xc5, (byte)0xf3, (byte)0xdb, (byte)0x47, (byte)0xe5, (byte)0xa5, (byte)0x9c, (byte)0x77,
+ (byte)0xa, (byte)0xa6, (byte)0x20, (byte)0x68, (byte)0xfe, (byte)0x7f, (byte)0xc1, (byte)0xad
+ };
+
+ private const int BLOCK_SIZE = 8;
+
+ private int[] workingKey;
+ private bool encrypting;
+
+ private int[] GenerateWorkingKey(
+ byte[] key,
+ int bits)
+ {
+ int x;
+ int[] xKey = new int[128];
+
+ for (int i = 0; i != key.Length; i++)
+ {
+ xKey[i] = key[i] & 0xff;
+ }
+
+ // Phase 1: Expand input key to 128 bytes
+ int len = key.Length;
+
+ if (len < 128)
+ {
+ int index = 0;
+
+ x = xKey[len - 1];
+
+ do
+ {
+ x = piTable[(x + xKey[index++]) & 255] & 0xff;
+ xKey[len++] = x;
+ }
+ while (len < 128);
+ }
+
+ // Phase 2 - reduce effective key size to "bits"
+ len = (bits + 7) >> 3;
+ x = piTable[xKey[128 - len] & (255 >> (7 & -bits))] & 0xff;
+ xKey[128 - len] = x;
+
+ for (int i = 128 - len - 1; i >= 0; i--)
+ {
+ x = piTable[x ^ xKey[i + len]] & 0xff;
+ xKey[i] = x;
+ }
+
+ // Phase 3 - copy to newKey in little-endian order
+ int[] newKey = new int[64];
+
+ for (int i = 0; i != newKey.Length; i++)
+ {
+ newKey[i] = (xKey[2 * i] + (xKey[2 * i + 1] << 8));
+ }
+
+ return newKey;
+ }
+
+ /**
+ * initialise a RC2 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.encrypting = forEncryption;
+
+ if (parameters is RC2Parameters)
+ {
+ RC2Parameters param = (RC2Parameters) parameters;
+
+ workingKey = GenerateWorkingKey(param.GetKey(), param.EffectiveKeyBits);
+ }
+ else if (parameters is KeyParameter)
+ {
+ KeyParameter param = (KeyParameter) parameters;
+ byte[] key = param.GetKey();
+
+ workingKey = GenerateWorkingKey(key, key.Length * 8);
+ }
+ else
+ {
+ throw new ArgumentException("invalid parameter passed to RC2 init - " + parameters.GetType().Name);
+ }
+ }
+
+ public void Reset()
+ {
+ }
+
+ public string AlgorithmName
+ {
+ get { return "RC2"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (workingKey == null)
+ throw new InvalidOperationException("RC2 engine not initialised");
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ if (encrypting)
+ {
+ EncryptBlock(input, inOff, output, outOff);
+ }
+ else
+ {
+ DecryptBlock(input, inOff, output, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * return the result rotating the 16 bit number in x left by y
+ */
+ private int RotateWordLeft(
+ int x,
+ int y)
+ {
+ x &= 0xffff;
+ return (x << y) | (x >> (16 - y));
+ }
+
+ private void EncryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ int x76, x54, x32, x10;
+
+ x76 = ((input[inOff + 7] & 0xff) << 8) + (input[inOff + 6] & 0xff);
+ x54 = ((input[inOff + 5] & 0xff) << 8) + (input[inOff + 4] & 0xff);
+ x32 = ((input[inOff + 3] & 0xff) << 8) + (input[inOff + 2] & 0xff);
+ x10 = ((input[inOff + 1] & 0xff) << 8) + (input[inOff + 0] & 0xff);
+
+ for (int i = 0; i <= 16; i += 4)
+ {
+ x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ x10 += workingKey[x76 & 63];
+ x32 += workingKey[x10 & 63];
+ x54 += workingKey[x32 & 63];
+ x76 += workingKey[x54 & 63];
+
+ for (int i = 20; i <= 40; i += 4)
+ {
+ x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ x10 += workingKey[x76 & 63];
+ x32 += workingKey[x10 & 63];
+ x54 += workingKey[x32 & 63];
+ x76 += workingKey[x54 & 63];
+
+ for (int i = 44; i < 64; i += 4)
+ {
+ x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ outBytes[outOff + 0] = (byte)x10;
+ outBytes[outOff + 1] = (byte)(x10 >> 8);
+ outBytes[outOff + 2] = (byte)x32;
+ outBytes[outOff + 3] = (byte)(x32 >> 8);
+ outBytes[outOff + 4] = (byte)x54;
+ outBytes[outOff + 5] = (byte)(x54 >> 8);
+ outBytes[outOff + 6] = (byte)x76;
+ outBytes[outOff + 7] = (byte)(x76 >> 8);
+ }
+
+ private void DecryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ int x76, x54, x32, x10;
+
+ x76 = ((input[inOff + 7] & 0xff) << 8) + (input[inOff + 6] & 0xff);
+ x54 = ((input[inOff + 5] & 0xff) << 8) + (input[inOff + 4] & 0xff);
+ x32 = ((input[inOff + 3] & 0xff) << 8) + (input[inOff + 2] & 0xff);
+ x10 = ((input[inOff + 1] & 0xff) << 8) + (input[inOff + 0] & 0xff);
+
+ for (int i = 60; i >= 44; i -= 4)
+ {
+ x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ x76 -= workingKey[x54 & 63];
+ x54 -= workingKey[x32 & 63];
+ x32 -= workingKey[x10 & 63];
+ x10 -= workingKey[x76 & 63];
+
+ for (int i = 40; i >= 20; i -= 4)
+ {
+ x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ x76 -= workingKey[x54 & 63];
+ x54 -= workingKey[x32 & 63];
+ x32 -= workingKey[x10 & 63];
+ x10 -= workingKey[x76 & 63];
+
+ for (int i = 16; i >= 0; i -= 4)
+ {
+ x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ outBytes[outOff + 0] = (byte)x10;
+ outBytes[outOff + 1] = (byte)(x10 >> 8);
+ outBytes[outOff + 2] = (byte)x32;
+ outBytes[outOff + 3] = (byte)(x32 >> 8);
+ outBytes[outOff + 4] = (byte)x54;
+ outBytes[outOff + 5] = (byte)(x54 >> 8);
+ outBytes[outOff + 6] = (byte)x76;
+ outBytes[outOff + 7] = (byte)(x76 >> 8);
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/RC2WrapEngine.cs b/Crypto/src/crypto/engines/RC2WrapEngine.cs
new file mode 100644
index 000000000..238c9f76a
--- /dev/null
+++ b/Crypto/src/crypto/engines/RC2WrapEngine.cs
@@ -0,0 +1,370 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * Wrap keys according to RFC 3217 - RC2 mechanism
+ */
+ public class RC2WrapEngine
+ : IWrapper
+ {
+ /** Field engine */
+ private CbcBlockCipher engine;
+
+ /** Field param */
+ private ICipherParameters parameters;
+
+ /** Field paramPlusIV */
+ private ParametersWithIV paramPlusIV;
+
+ /** Field iv */
+ private byte[] iv;
+
+ /** Field forWrapping */
+ private bool forWrapping;
+
+ private SecureRandom sr;
+
+ /** Field IV2 */
+ private static readonly byte[] IV2 =
+ {
+ (byte) 0x4a, (byte) 0xdd, (byte) 0xa2,
+ (byte) 0x2c, (byte) 0x79, (byte) 0xe8,
+ (byte) 0x21, (byte) 0x05
+ };
+
+ //
+ // checksum digest
+ //
+ IDigest sha1 = new Sha1Digest();
+ byte[] digest = new byte[20];
+
+ /**
+ * Method init
+ *
+ * @param forWrapping
+ * @param param
+ */
+ public void Init(
+ bool forWrapping,
+ ICipherParameters parameters)
+ {
+ this.forWrapping = forWrapping;
+ this.engine = new CbcBlockCipher(new RC2Engine());
+
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom pWithR = (ParametersWithRandom)parameters;
+ sr = pWithR.Random;
+ parameters = pWithR.Parameters;
+ }
+ else
+ {
+ sr = new SecureRandom();
+ }
+
+ if (parameters is ParametersWithIV)
+ {
+ if (!forWrapping)
+ throw new ArgumentException("You should not supply an IV for unwrapping");
+
+ this.paramPlusIV = (ParametersWithIV)parameters;
+ this.iv = this.paramPlusIV.GetIV();
+ this.parameters = this.paramPlusIV.Parameters;
+
+ if (this.iv.Length != 8)
+ throw new ArgumentException("IV is not 8 octets");
+ }
+ else
+ {
+ this.parameters = parameters;
+
+ if (this.forWrapping)
+ {
+ // Hm, we have no IV but we want to wrap ?!?
+ // well, then we have to create our own IV.
+ this.iv = new byte[8];
+ sr.NextBytes(iv);
+ this.paramPlusIV = new ParametersWithIV(this.parameters, this.iv);
+ }
+ }
+ }
+
+ /**
+ * Method GetAlgorithmName
+ *
+ * @return
+ */
+ public string AlgorithmName
+ {
+ get { return "RC2"; }
+ }
+
+ /**
+ * Method wrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return
+ */
+ public byte[] Wrap(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (!forWrapping)
+ {
+ throw new InvalidOperationException("Not initialized for wrapping");
+ }
+
+ int len = length + 1;
+ if ((len % 8) != 0)
+ {
+ len += 8 - (len % 8);
+ }
+
+ byte [] keyToBeWrapped = new byte[len];
+
+ keyToBeWrapped[0] = (byte)length;
+ Array.Copy(input, inOff, keyToBeWrapped, 1, length);
+
+ byte[] pad = new byte[keyToBeWrapped.Length - length - 1];
+
+ if (pad.Length > 0)
+ {
+ sr.NextBytes(pad);
+ Array.Copy(pad, 0, keyToBeWrapped, length + 1, pad.Length);
+ }
+
+ // Compute the CMS Key Checksum, (section 5.6.1), call this CKS.
+ byte[] CKS = CalculateCmsKeyChecksum(keyToBeWrapped);
+
+ // Let WKCKS = WK || CKS where || is concatenation.
+ byte[] WKCKS = new byte[keyToBeWrapped.Length + CKS.Length];
+
+ Array.Copy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.Length);
+ Array.Copy(CKS, 0, WKCKS, keyToBeWrapped.Length, CKS.Length);
+
+ // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
+ // initialization vector. Call the results TEMP1.
+ byte [] TEMP1 = new byte[WKCKS.Length];
+
+ Array.Copy(WKCKS, 0, TEMP1, 0, WKCKS.Length);
+
+ int noOfBlocks = WKCKS.Length / engine.GetBlockSize();
+ int extraBytes = WKCKS.Length % engine.GetBlockSize();
+
+ if (extraBytes != 0)
+ {
+ throw new InvalidOperationException("Not multiple of block length");
+ }
+
+ engine.Init(true, paramPlusIV);
+
+ for (int i = 0; i < noOfBlocks; i++)
+ {
+ int currentBytePos = i * engine.GetBlockSize();
+
+ engine.ProcessBlock(TEMP1, currentBytePos, TEMP1, currentBytePos);
+ }
+
+ // Left TEMP2 = IV || TEMP1.
+ byte[] TEMP2 = new byte[this.iv.Length + TEMP1.Length];
+
+ Array.Copy(this.iv, 0, TEMP2, 0, this.iv.Length);
+ Array.Copy(TEMP1, 0, TEMP2, this.iv.Length, TEMP1.Length);
+
+ // Reverse the order of the octets in TEMP2 and call the result TEMP3.
+ byte[] TEMP3 = new byte[TEMP2.Length];
+
+ for (int i = 0; i < TEMP2.Length; i++)
+ {
+ TEMP3[i] = TEMP2[TEMP2.Length - (i + 1)];
+ }
+
+ // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
+ // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
+ // result. It is 40 octets long if a 168 bit key is being wrapped.
+ ParametersWithIV param2 = new ParametersWithIV(this.parameters, IV2);
+
+ this.engine.Init(true, param2);
+
+ for (int i = 0; i < noOfBlocks + 1; i++)
+ {
+ int currentBytePos = i * engine.GetBlockSize();
+
+ engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ return TEMP3;
+ }
+
+ /**
+ * Method unwrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return
+ * @throws InvalidCipherTextException
+ */
+ public byte[] Unwrap(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (forWrapping)
+ {
+ throw new InvalidOperationException("Not set for unwrapping");
+ }
+
+ if (input == null)
+ {
+ throw new InvalidCipherTextException("Null pointer as ciphertext");
+ }
+
+ if (length % engine.GetBlockSize() != 0)
+ {
+ throw new InvalidCipherTextException("Ciphertext not multiple of "
+ + engine.GetBlockSize());
+ }
+
+ /*
+ // Check if the length of the cipher text is reasonable given the key
+ // type. It must be 40 bytes for a 168 bit key and either 32, 40, or
+ // 48 bytes for a 128, 192, or 256 bit key. If the length is not supported
+ // or inconsistent with the algorithm for which the key is intended,
+ // return error.
+ //
+ // we do not accept 168 bit keys. it has to be 192 bit.
+ int lengthA = (estimatedKeyLengthInBit / 8) + 16;
+ int lengthB = estimatedKeyLengthInBit % 8;
+
+ if ((lengthA != keyToBeUnwrapped.Length) || (lengthB != 0)) {
+ throw new XMLSecurityException("empty");
+ }
+ */
+
+ // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK
+ // and an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3.
+ ParametersWithIV param2 = new ParametersWithIV(this.parameters, IV2);
+
+ this.engine.Init(false, param2);
+
+ byte [] TEMP3 = new byte[length];
+
+ Array.Copy(input, inOff, TEMP3, 0, length);
+
+ for (int i = 0; i < (TEMP3.Length / engine.GetBlockSize()); i++)
+ {
+ int currentBytePos = i * engine.GetBlockSize();
+
+ engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ // Reverse the order of the octets in TEMP3 and call the result TEMP2.
+ byte[] TEMP2 = new byte[TEMP3.Length];
+
+ for (int i = 0; i < TEMP3.Length; i++)
+ {
+ TEMP2[i] = TEMP3[TEMP3.Length - (i + 1)];
+ }
+
+ // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
+ this.iv = new byte[8];
+
+ byte[] TEMP1 = new byte[TEMP2.Length - 8];
+
+ Array.Copy(TEMP2, 0, this.iv, 0, 8);
+ Array.Copy(TEMP2, 8, TEMP1, 0, TEMP2.Length - 8);
+
+ // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV
+ // found in the previous step. Call the result WKCKS.
+ this.paramPlusIV = new ParametersWithIV(this.parameters, this.iv);
+
+ this.engine.Init(false, this.paramPlusIV);
+
+ byte[] LCEKPADICV = new byte[TEMP1.Length];
+
+ Array.Copy(TEMP1, 0, LCEKPADICV, 0, TEMP1.Length);
+
+ for (int i = 0; i < (LCEKPADICV.Length / engine.GetBlockSize()); i++)
+ {
+ int currentBytePos = i * engine.GetBlockSize();
+
+ engine.ProcessBlock(LCEKPADICV, currentBytePos, LCEKPADICV, currentBytePos);
+ }
+
+ // Decompose LCEKPADICV. CKS is the last 8 octets and WK, the wrapped key, are
+ // those octets before the CKS.
+ byte[] result = new byte[LCEKPADICV.Length - 8];
+ byte[] CKStoBeVerified = new byte[8];
+
+ Array.Copy(LCEKPADICV, 0, result, 0, LCEKPADICV.Length - 8);
+ Array.Copy(LCEKPADICV, LCEKPADICV.Length - 8, CKStoBeVerified, 0, 8);
+
+ // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and compare
+ // with the CKS extracted in the above step. If they are not equal, return error.
+ if (!CheckCmsKeyChecksum(result, CKStoBeVerified))
+ {
+ throw new InvalidCipherTextException(
+ "Checksum inside ciphertext is corrupted");
+ }
+
+ if ((result.Length - ((result[0] & 0xff) + 1)) > 7)
+ {
+ throw new InvalidCipherTextException(
+ "too many pad bytes (" + (result.Length - ((result[0] & 0xff) + 1)) + ")");
+ }
+
+ // CEK is the wrapped key, now extracted for use in data decryption.
+ byte[] CEK = new byte[result[0]];
+ Array.Copy(result, 1, CEK, 0, CEK.Length);
+ return CEK;
+ }
+
+ /**
+ * Some key wrap algorithms make use of the Key Checksum defined
+ * in CMS [CMS-Algorithms]. This is used to provide an integrity
+ * check value for the key being wrapped. The algorithm is
+ *
+ * - Compute the 20 octet SHA-1 hash on the key being wrapped.
+ * - Use the first 8 octets of this hash as the checksum value.
+ *
+ * @param key
+ * @return
+ * @throws Exception
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private byte[] CalculateCmsKeyChecksum(
+ byte[] key)
+ {
+ sha1.BlockUpdate(key, 0, key.Length);
+ sha1.DoFinal(digest, 0);
+
+ byte[] result = new byte[8];
+ Array.Copy(digest, 0, result, 0, 8);
+ return result;
+ }
+
+ /**
+ * @param key
+ * @param checksum
+ * @return
+ * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ */
+ private bool CheckCmsKeyChecksum(
+ byte[] key,
+ byte[] checksum)
+ {
+ return Arrays.ConstantTimeAreEqual(CalculateCmsKeyChecksum(key), checksum);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/RC4Engine.cs b/Crypto/src/crypto/engines/RC4Engine.cs
new file mode 100644
index 000000000..c65468d93
--- /dev/null
+++ b/Crypto/src/crypto/engines/RC4Engine.cs
@@ -0,0 +1,147 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ public class RC4Engine
+ : IStreamCipher
+ {
+ private readonly static int STATE_LENGTH = 256;
+
+ /*
+ * variables to hold the state of the RC4 engine
+ * during encryption and decryption
+ */
+
+ private byte[] engineState;
+ private int x;
+ private int y;
+ private byte[] workingKey;
+
+ /**
+ * initialise a RC4 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (parameters is KeyParameter)
+ {
+ /*
+ * RC4 encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant.
+ */
+ workingKey = ((KeyParameter)parameters).GetKey();
+ SetKey(workingKey);
+
+ return;
+ }
+
+ throw new ArgumentException("invalid parameter passed to RC4 init - " + parameters.GetType().ToString());
+ }
+
+ public string AlgorithmName
+ {
+ get { return "RC4"; }
+ }
+
+ public byte ReturnByte(
+ byte input)
+ {
+ x = (x + 1) & 0xff;
+ y = (engineState[x] + y) & 0xff;
+
+ // swap
+ byte tmp = engineState[x];
+ engineState[x] = engineState[y];
+ engineState[y] = tmp;
+
+ // xor
+ return (byte)(input ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
+ }
+
+ public void ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff
+ )
+ {
+ if ((inOff + length) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + length) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < length ; i++)
+ {
+ x = (x + 1) & 0xff;
+ y = (engineState[x] + y) & 0xff;
+
+ // swap
+ byte tmp = engineState[x];
+ engineState[x] = engineState[y];
+ engineState[y] = tmp;
+
+ // xor
+ output[i+outOff] = (byte)(input[i + inOff]
+ ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
+ }
+ }
+
+ public void Reset()
+ {
+ SetKey(workingKey);
+ }
+
+ // Private implementation
+
+ private void SetKey(
+ byte[] keyBytes)
+ {
+ workingKey = keyBytes;
+
+ // System.out.println("the key length is ; "+ workingKey.Length);
+
+ x = 0;
+ y = 0;
+
+ if (engineState == null)
+ {
+ engineState = new byte[STATE_LENGTH];
+ }
+
+ // reset the state of the engine
+ for (int i=0; i < STATE_LENGTH; i++)
+ {
+ engineState[i] = (byte)i;
+ }
+
+ int i1 = 0;
+ int i2 = 0;
+
+ for (int i=0; i < STATE_LENGTH; i++)
+ {
+ i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff;
+ // do the byte-swap inline
+ byte tmp = engineState[i];
+ engineState[i] = engineState[i2];
+ engineState[i2] = tmp;
+ i1 = (i1+1) % keyBytes.Length;
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/RC532Engine.cs b/Crypto/src/crypto/engines/RC532Engine.cs
new file mode 100644
index 000000000..1661707ef
--- /dev/null
+++ b/Crypto/src/crypto/engines/RC532Engine.cs
@@ -0,0 +1,294 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * The specification for RC5 came from the <code>RC5 Encryption Algorithm</code>
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * <em>http://www.rsasecurity.com/rsalabs/cryptobytes</em>.
+ * <p>
+ * This implementation has a word size of 32 bits.</p>
+ */
+ public class RC532Engine
+ : IBlockCipher
+ {
+ /*
+ * the number of rounds to perform
+ */
+ private int _noRounds;
+
+ /*
+ * the expanded key array of size 2*(rounds + 1)
+ */
+ private int [] _S;
+
+ /*
+ * our "magic constants" for 32 32
+ *
+ * Pw = Odd((e-2) * 2^wordsize)
+ * Qw = Odd((o-2) * 2^wordsize)
+ *
+ * where e is the base of natural logarithms (2.718281828...)
+ * and o is the golden ratio (1.61803398...)
+ */
+ private static readonly int P32 = unchecked((int) 0xb7e15163);
+ private static readonly int Q32 = unchecked((int) 0x9e3779b9);
+
+ private bool forEncryption;
+
+ /**
+ * Create an instance of the RC5 encryption algorithm
+ * and set some defaults
+ */
+ public RC532Engine()
+ {
+ _noRounds = 12; // the default
+// _S = null;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "RC5-32"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return 2 * 4;
+ }
+
+ /**
+ * initialise a RC5-32 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (typeof(RC5Parameters).IsInstanceOfType(parameters))
+ {
+ RC5Parameters p = (RC5Parameters)parameters;
+
+ _noRounds = p.Rounds;
+
+ SetKey(p.GetKey());
+ }
+ else if (typeof(KeyParameter).IsInstanceOfType(parameters))
+ {
+ KeyParameter p = (KeyParameter)parameters;
+
+ SetKey(p.GetKey());
+ }
+ else
+ {
+ throw new ArgumentException("invalid parameter passed to RC532 init - " + parameters.GetType().ToString());
+ }
+
+ this.forEncryption = forEncryption;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ return (forEncryption)
+ ? EncryptBlock(input, inOff, output, outOff)
+ : DecryptBlock(input, inOff, output, outOff);
+ }
+
+ public void Reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ *
+ * @param key the key to be used
+ */
+ private void SetKey(
+ byte[] key)
+ {
+ //
+ // KEY EXPANSION:
+ //
+ // There are 3 phases to the key expansion.
+ //
+ // Phase 1:
+ // Copy the secret key K[0...b-1] into an array L[0..c-1] of
+ // c = ceil(b/u), where u = 32/8 in little-endian order.
+ // In other words, we fill up L using u consecutive key bytes
+ // of K. Any unfilled byte positions in L are zeroed. In the
+ // case that b = c = 0, set c = 1 and L[0] = 0.
+ //
+ int[] L = new int[(key.Length + (4 - 1)) / 4];
+
+ for (int i = 0; i != key.Length; i++)
+ {
+ L[i / 4] += (key[i] & 0xff) << (8 * (i % 4));
+ }
+
+ //
+ // Phase 2:
+ // Initialize S to a particular fixed pseudo-random bit pattern
+ // using an arithmetic progression modulo 2^wordsize determined
+ // by the magic numbers, Pw & Qw.
+ //
+ _S = new int[2*(_noRounds + 1)];
+
+ _S[0] = P32;
+ for (int i=1; i < _S.Length; i++)
+ {
+ _S[i] = (_S[i-1] + Q32);
+ }
+
+ //
+ // Phase 3:
+ // Mix in the user's secret key in 3 passes over the arrays S & L.
+ // The max of the arrays sizes is used as the loop control
+ //
+ int iter;
+
+ if (L.Length > _S.Length)
+ {
+ iter = 3 * L.Length;
+ }
+ else
+ {
+ iter = 3 * _S.Length;
+ }
+
+ int A = 0, B = 0;
+ int ii = 0, jj = 0;
+
+ for (int k = 0; k < iter; k++)
+ {
+ A = _S[ii] = RotateLeft(_S[ii] + A + B, 3);
+ B = L[jj] = RotateLeft( L[jj] + A + B, A+B);
+ ii = (ii+1) % _S.Length;
+ jj = (jj+1) % L.Length;
+ }
+ }
+
+ /**
+ * Encrypt the given block starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param in in byte buffer containing data to encrypt
+ * @param inOff offset into src buffer
+ * @param out out buffer where encrypted data is written
+ * @param outOff offset into out buffer
+ */
+ private int EncryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ int A = BytesToWord(input, inOff) + _S[0];
+ int B = BytesToWord(input, inOff + 4) + _S[1];
+
+ for (int i = 1; i <= _noRounds; i++)
+ {
+ A = RotateLeft(A ^ B, B) + _S[2*i];
+ B = RotateLeft(B ^ A, A) + _S[2*i+1];
+ }
+
+ WordToBytes(A, outBytes, outOff);
+ WordToBytes(B, outBytes, outOff + 4);
+
+ return 2 * 4;
+ }
+
+ private int DecryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ int A = BytesToWord(input, inOff);
+ int B = BytesToWord(input, inOff + 4);
+
+ for (int i = _noRounds; i >= 1; i--)
+ {
+ B = RotateRight(B - _S[2*i+1], A) ^ A;
+ A = RotateRight(A - _S[2*i], B) ^ B;
+ }
+
+ WordToBytes(A - _S[0], outBytes, outOff);
+ WordToBytes(B - _S[1], outBytes, outOff + 4);
+
+ return 2 * 4;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ //
+ // PRIVATE Helper Methods
+ //
+ //////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a left "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(32)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ *
+ * @param x word to rotate
+ * @param y number of bits to rotate % 32
+ */
+ private int RotateLeft(int x, int y) {
+ return ((int) ( (uint) (x << (y & (32-1))) |
+ ((uint) x >> (32 - (y & (32-1)))) )
+ );
+ }
+
+ /**
+ * Perform a right "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(32)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ *
+ * @param x word to rotate
+ * @param y number of bits to rotate % 32
+ */
+ private int RotateRight(int x, int y) {
+ return ((int) ( ((uint) x >> (y & (32-1))) |
+ (uint) (x << (32 - (y & (32-1)))) )
+ );
+ }
+
+ private int BytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ return (src[srcOff] & 0xff) | ((src[srcOff + 1] & 0xff) << 8)
+ | ((src[srcOff + 2] & 0xff) << 16) | ((src[srcOff + 3] & 0xff) << 24);
+ }
+
+ private void WordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ dst[dstOff] = (byte)word;
+ dst[dstOff + 1] = (byte)(word >> 8);
+ dst[dstOff + 2] = (byte)(word >> 16);
+ dst[dstOff + 3] = (byte)(word >> 24);
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/RC564Engine.cs b/Crypto/src/crypto/engines/RC564Engine.cs
new file mode 100644
index 000000000..5c69d40ff
--- /dev/null
+++ b/Crypto/src/crypto/engines/RC564Engine.cs
@@ -0,0 +1,295 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * The specification for RC5 came from the <code>RC5 Encryption Algorithm</code>
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * <em>http://www.rsasecurity.com/rsalabs/cryptobytes</em>.
+ * <p>
+ * This implementation is set to work with a 64 bit word size.</p>
+ */
+ public class RC564Engine
+ : IBlockCipher
+ {
+ private static readonly int wordSize = 64;
+ private static readonly int bytesPerWord = wordSize / 8;
+
+ /*
+ * the number of rounds to perform
+ */
+ private int _noRounds;
+
+ /*
+ * the expanded key array of size 2*(rounds + 1)
+ */
+ private long [] _S;
+
+ /*
+ * our "magic constants" for wordSize 62
+ *
+ * Pw = Odd((e-2) * 2^wordsize)
+ * Qw = Odd((o-2) * 2^wordsize)
+ *
+ * where e is the base of natural logarithms (2.718281828...)
+ * and o is the golden ratio (1.61803398...)
+ */
+ private static readonly long P64 = unchecked( (long) 0xb7e151628aed2a6bL);
+ private static readonly long Q64 = unchecked( (long) 0x9e3779b97f4a7c15L);
+
+ private bool forEncryption;
+
+ /**
+ * Create an instance of the RC5 encryption algorithm
+ * and set some defaults
+ */
+ public RC564Engine()
+ {
+ _noRounds = 12;
+// _S = null;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "RC5-64"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return 2 * bytesPerWord;
+ }
+
+ /**
+ * initialise a RC5-64 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(typeof(RC5Parameters).IsInstanceOfType(parameters)))
+ {
+ throw new ArgumentException("invalid parameter passed to RC564 init - " + parameters.GetType().ToString());
+ }
+
+ RC5Parameters p = (RC5Parameters)parameters;
+
+ this.forEncryption = forEncryption;
+
+ _noRounds = p.Rounds;
+
+ SetKey(p.GetKey());
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ return (forEncryption) ? EncryptBlock(input, inOff, output, outOff)
+ : DecryptBlock(input, inOff, output, outOff);
+ }
+
+ public void Reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ *
+ * @param key the key to be used
+ */
+ private void SetKey(
+ byte[] key)
+ {
+ //
+ // KEY EXPANSION:
+ //
+ // There are 3 phases to the key expansion.
+ //
+ // Phase 1:
+ // Copy the secret key K[0...b-1] into an array L[0..c-1] of
+ // c = ceil(b/u), where u = wordSize/8 in little-endian order.
+ // In other words, we fill up L using u consecutive key bytes
+ // of K. Any unfilled byte positions in L are zeroed. In the
+ // case that b = c = 0, set c = 1 and L[0] = 0.
+ //
+ long[] L = new long[(key.Length + (bytesPerWord - 1)) / bytesPerWord];
+
+ for (int i = 0; i != key.Length; i++)
+ {
+ L[i / bytesPerWord] += (long)(key[i] & 0xff) << (8 * (i % bytesPerWord));
+ }
+
+ //
+ // Phase 2:
+ // Initialize S to a particular fixed pseudo-random bit pattern
+ // using an arithmetic progression modulo 2^wordsize determined
+ // by the magic numbers, Pw & Qw.
+ //
+ _S = new long[2*(_noRounds + 1)];
+
+ _S[0] = P64;
+ for (int i=1; i < _S.Length; i++)
+ {
+ _S[i] = (_S[i-1] + Q64);
+ }
+
+ //
+ // Phase 3:
+ // Mix in the user's secret key in 3 passes over the arrays S & L.
+ // The max of the arrays sizes is used as the loop control
+ //
+ int iter;
+
+ if (L.Length > _S.Length)
+ {
+ iter = 3 * L.Length;
+ }
+ else
+ {
+ iter = 3 * _S.Length;
+ }
+
+ long A = 0, B = 0;
+ int ii = 0, jj = 0;
+
+ for (int k = 0; k < iter; k++)
+ {
+ A = _S[ii] = RotateLeft(_S[ii] + A + B, 3);
+ B = L[jj] = RotateLeft( L[jj] + A + B, A+B);
+ ii = (ii+1) % _S.Length;
+ jj = (jj+1) % L.Length;
+ }
+ }
+
+ /**
+ * Encrypt the given block starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param in in byte buffer containing data to encrypt
+ * @param inOff offset into src buffer
+ * @param out out buffer where encrypted data is written
+ * @param outOff offset into out buffer
+ */
+ private int EncryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ long A = BytesToWord(input, inOff) + _S[0];
+ long B = BytesToWord(input, inOff + bytesPerWord) + _S[1];
+
+ for (int i = 1; i <= _noRounds; i++)
+ {
+ A = RotateLeft(A ^ B, B) + _S[2*i];
+ B = RotateLeft(B ^ A, A) + _S[2*i+1];
+ }
+
+ WordToBytes(A, outBytes, outOff);
+ WordToBytes(B, outBytes, outOff + bytesPerWord);
+
+ return 2 * bytesPerWord;
+ }
+
+ private int DecryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ long A = BytesToWord(input, inOff);
+ long B = BytesToWord(input, inOff + bytesPerWord);
+
+ for (int i = _noRounds; i >= 1; i--)
+ {
+ B = RotateRight(B - _S[2*i+1], A) ^ A;
+ A = RotateRight(A - _S[2*i], B) ^ B;
+ }
+
+ WordToBytes(A - _S[0], outBytes, outOff);
+ WordToBytes(B - _S[1], outBytes, outOff + bytesPerWord);
+
+ return 2 * bytesPerWord;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ //
+ // PRIVATE Helper Methods
+ //
+ //////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a left "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ *
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private long RotateLeft(long x, long y) {
+ return ((long) ( (ulong) (x << (int) (y & (wordSize-1))) |
+ ((ulong) x >> (int) (wordSize - (y & (wordSize-1)))))
+ );
+ }
+
+ /**
+ * Perform a right "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ *
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private long RotateRight(long x, long y) {
+ return ((long) ( ((ulong) x >> (int) (y & (wordSize-1))) |
+ (ulong) (x << (int) (wordSize - (y & (wordSize-1)))))
+ );
+ }
+
+ private long BytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ long word = 0;
+
+ for (int i = bytesPerWord - 1; i >= 0; i--)
+ {
+ word = (word << 8) + (src[i + srcOff] & 0xff);
+ }
+
+ return word;
+ }
+
+ private void WordToBytes(
+ long word,
+ byte[] dst,
+ int dstOff)
+ {
+ for (int i = 0; i < bytesPerWord; i++)
+ {
+ dst[i + dstOff] = (byte)word;
+ word = (long) ((ulong) word >> 8);
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/RC6Engine.cs b/Crypto/src/crypto/engines/RC6Engine.cs
new file mode 100644
index 000000000..d72cc2f7b
--- /dev/null
+++ b/Crypto/src/crypto/engines/RC6Engine.cs
@@ -0,0 +1,362 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * An RC6 engine.
+ */
+ public class RC6Engine
+ : IBlockCipher
+ {
+ private static readonly int wordSize = 32;
+ private static readonly int bytesPerWord = wordSize / 8;
+
+ /*
+ * the number of rounds to perform
+ */
+ private static readonly int _noRounds = 20;
+
+ /*
+ * the expanded key array of size 2*(rounds + 1)
+ */
+ private int [] _S;
+
+ /*
+ * our "magic constants" for wordSize 32
+ *
+ * Pw = Odd((e-2) * 2^wordsize)
+ * Qw = Odd((o-2) * 2^wordsize)
+ *
+ * where e is the base of natural logarithms (2.718281828...)
+ * and o is the golden ratio (1.61803398...)
+ */
+ private static readonly int P32 = unchecked((int) 0xb7e15163);
+ private static readonly int Q32 = unchecked((int) 0x9e3779b9);
+
+ private static readonly int LGW = 5; // log2(32)
+
+ private bool forEncryption;
+
+ /**
+ * Create an instance of the RC6 encryption algorithm
+ * and set some defaults
+ */
+ public RC6Engine()
+ {
+// _S = null;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "RC6"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return 4 * bytesPerWord;
+ }
+
+ /**
+ * initialise a RC5-32 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("invalid parameter passed to RC6 init - " + parameters.GetType().ToString());
+
+ this.forEncryption = forEncryption;
+
+ KeyParameter p = (KeyParameter)parameters;
+ SetKey(p.GetKey());
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ int blockSize = GetBlockSize();
+ if (_S == null)
+ throw new InvalidOperationException("RC6 engine not initialised");
+ if ((inOff + blockSize) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + blockSize) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ return (forEncryption)
+ ? EncryptBlock(input, inOff, output, outOff)
+ : DecryptBlock(input, inOff, output, outOff);
+ }
+
+ public void Reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ *
+ * @param inKey the key to be used
+ */
+ private void SetKey(
+ byte[] key)
+ {
+ //
+ // KEY EXPANSION:
+ //
+ // There are 3 phases to the key expansion.
+ //
+ // Phase 1:
+ // Copy the secret key K[0...b-1] into an array L[0..c-1] of
+ // c = ceil(b/u), where u = wordSize/8 in little-endian order.
+ // In other words, we fill up L using u consecutive key bytes
+ // of K. Any unfilled byte positions in L are zeroed. In the
+ // case that b = c = 0, set c = 1 and L[0] = 0.
+ //
+ // compute number of dwords
+ int c = (key.Length + (bytesPerWord - 1)) / bytesPerWord;
+ if (c == 0)
+ {
+ c = 1;
+ }
+ int[] L = new int[(key.Length + bytesPerWord - 1) / bytesPerWord];
+
+ // load all key bytes into array of key dwords
+ for (int i = key.Length - 1; i >= 0; i--)
+ {
+ L[i / bytesPerWord] = (L[i / bytesPerWord] << 8) + (key[i] & 0xff);
+ }
+
+ //
+ // Phase 2:
+ // Key schedule is placed in a array of 2+2*ROUNDS+2 = 44 dwords.
+ // Initialize S to a particular fixed pseudo-random bit pattern
+ // using an arithmetic progression modulo 2^wordsize determined
+ // by the magic numbers, Pw & Qw.
+ //
+ _S = new int[2+2*_noRounds+2];
+
+ _S[0] = P32;
+ for (int i=1; i < _S.Length; i++)
+ {
+ _S[i] = (_S[i-1] + Q32);
+ }
+
+ //
+ // Phase 3:
+ // Mix in the user's secret key in 3 passes over the arrays S & L.
+ // The max of the arrays sizes is used as the loop control
+ //
+ int iter;
+
+ if (L.Length > _S.Length)
+ {
+ iter = 3 * L.Length;
+ }
+ else
+ {
+ iter = 3 * _S.Length;
+ }
+
+ int A = 0;
+ int B = 0;
+ int ii = 0, jj = 0;
+
+ for (int k = 0; k < iter; k++)
+ {
+ A = _S[ii] = RotateLeft(_S[ii] + A + B, 3);
+ B = L[jj] = RotateLeft( L[jj] + A + B, A+B);
+ ii = (ii+1) % _S.Length;
+ jj = (jj+1) % L.Length;
+ }
+ }
+
+ private int EncryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ // load A,B,C and D registers from in.
+ int A = BytesToWord(input, inOff);
+ int B = BytesToWord(input, inOff + bytesPerWord);
+ int C = BytesToWord(input, inOff + bytesPerWord*2);
+ int D = BytesToWord(input, inOff + bytesPerWord*3);
+
+ // Do pseudo-round #0: pre-whitening of B and D
+ B += _S[0];
+ D += _S[1];
+
+ // perform round #1,#2 ... #ROUNDS of encryption
+ for (int i = 1; i <= _noRounds; i++)
+ {
+ int t = 0,u = 0;
+
+ t = B*(2*B+1);
+ t = RotateLeft(t,5);
+
+ u = D*(2*D+1);
+ u = RotateLeft(u,5);
+
+ A ^= t;
+ A = RotateLeft(A,u);
+ A += _S[2*i];
+
+ C ^= u;
+ C = RotateLeft(C,t);
+ C += _S[2*i+1];
+
+ int temp = A;
+ A = B;
+ B = C;
+ C = D;
+ D = temp;
+ }
+ // do pseudo-round #(ROUNDS+1) : post-whitening of A and C
+ A += _S[2*_noRounds+2];
+ C += _S[2*_noRounds+3];
+
+ // store A, B, C and D registers to out
+ WordToBytes(A, outBytes, outOff);
+ WordToBytes(B, outBytes, outOff + bytesPerWord);
+ WordToBytes(C, outBytes, outOff + bytesPerWord*2);
+ WordToBytes(D, outBytes, outOff + bytesPerWord*3);
+
+ return 4 * bytesPerWord;
+ }
+
+ private int DecryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ // load A,B,C and D registers from out.
+ int A = BytesToWord(input, inOff);
+ int B = BytesToWord(input, inOff + bytesPerWord);
+ int C = BytesToWord(input, inOff + bytesPerWord*2);
+ int D = BytesToWord(input, inOff + bytesPerWord*3);
+
+ // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C
+ C -= _S[2*_noRounds+3];
+ A -= _S[2*_noRounds+2];
+
+ // Undo round #ROUNDS, .., #2,#1 of encryption
+ for (int i = _noRounds; i >= 1; i--)
+ {
+ int t=0,u = 0;
+
+ int temp = D;
+ D = C;
+ C = B;
+ B = A;
+ A = temp;
+
+ t = B*(2*B+1);
+ t = RotateLeft(t, LGW);
+
+ u = D*(2*D+1);
+ u = RotateLeft(u, LGW);
+
+ C -= _S[2*i+1];
+ C = RotateRight(C,t);
+ C ^= u;
+
+ A -= _S[2*i];
+ A = RotateRight(A,u);
+ A ^= t;
+
+ }
+ // Undo pseudo-round #0: pre-whitening of B and D
+ D -= _S[1];
+ B -= _S[0];
+
+ WordToBytes(A, outBytes, outOff);
+ WordToBytes(B, outBytes, outOff + bytesPerWord);
+ WordToBytes(C, outBytes, outOff + bytesPerWord*2);
+ WordToBytes(D, outBytes, outOff + bytesPerWord*3);
+
+ return 4 * bytesPerWord;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ //
+ // PRIVATE Helper Methods
+ //
+ //////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a left "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ *
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private int RotateLeft(int x, int y)
+ {
+ return ((int)((uint)(x << (y & (wordSize-1)))
+ | ((uint) x >> (wordSize - (y & (wordSize-1))))));
+ }
+
+ /**
+ * Perform a right "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ *
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private int RotateRight(int x, int y)
+ {
+ return ((int)(((uint) x >> (y & (wordSize-1)))
+ | (uint)(x << (wordSize - (y & (wordSize-1))))));
+ }
+
+ private int BytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ int word = 0;
+
+ for (int i = bytesPerWord - 1; i >= 0; i--)
+ {
+ word = (word << 8) + (src[i + srcOff] & 0xff);
+ }
+
+ return word;
+ }
+
+ private void WordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ for (int i = 0; i < bytesPerWord; i++)
+ {
+ dst[i + dstOff] = (byte)word;
+ word = (int) ((uint) word >> 8);
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/RFC3211WrapEngine.cs b/Crypto/src/crypto/engines/RFC3211WrapEngine.cs
new file mode 100644
index 000000000..e520075f9
--- /dev/null
+++ b/Crypto/src/crypto/engines/RFC3211WrapEngine.cs
@@ -0,0 +1,168 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * an implementation of the RFC 3211 Key Wrap
+ * Specification.
+ */
+ public class Rfc3211WrapEngine
+ : IWrapper
+ {
+ private CbcBlockCipher engine;
+ private ParametersWithIV param;
+ private bool forWrapping;
+ private SecureRandom rand;
+
+ public Rfc3211WrapEngine(
+ IBlockCipher engine)
+ {
+ this.engine = new CbcBlockCipher(engine);
+ }
+
+ public void Init(
+ bool forWrapping,
+ ICipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+
+ if (param is ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom) param;
+
+ this.rand = p.Random;
+ this.param = (ParametersWithIV) p.Parameters;
+ }
+ else
+ {
+ if (forWrapping)
+ {
+ rand = new SecureRandom();
+ }
+
+ this.param = (ParametersWithIV) param;
+ }
+ }
+
+ public string AlgorithmName
+ {
+ get { return engine.GetUnderlyingCipher().AlgorithmName + "/RFC3211Wrap"; }
+ }
+
+ public byte[] Wrap(
+ byte[] inBytes,
+ int inOff,
+ int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new InvalidOperationException("not set for wrapping");
+ }
+
+ engine.Init(true, param);
+
+ int blockSize = engine.GetBlockSize();
+ byte[] cekBlock;
+
+ if (inLen + 4 < blockSize * 2)
+ {
+ cekBlock = new byte[blockSize * 2];
+ }
+ else
+ {
+ cekBlock = new byte[(inLen + 4) % blockSize == 0 ? inLen + 4 : ((inLen + 4) / blockSize + 1) * blockSize];
+ }
+
+ cekBlock[0] = (byte)inLen;
+ cekBlock[1] = (byte)~inBytes[inOff];
+ cekBlock[2] = (byte)~inBytes[inOff + 1];
+ cekBlock[3] = (byte)~inBytes[inOff + 2];
+
+ Array.Copy(inBytes, inOff, cekBlock, 4, inLen);
+
+ rand.NextBytes(cekBlock, inLen + 4, cekBlock.Length - inLen - 4);
+
+ for (int i = 0; i < cekBlock.Length; i += blockSize)
+ {
+ engine.ProcessBlock(cekBlock, i, cekBlock, i);
+ }
+
+ for (int i = 0; i < cekBlock.Length; i += blockSize)
+ {
+ engine.ProcessBlock(cekBlock, i, cekBlock, i);
+ }
+
+ return cekBlock;
+ }
+
+ public byte[] Unwrap(
+ byte[] inBytes,
+ int inOff,
+ int inLen)
+ {
+ if (forWrapping)
+ {
+ throw new InvalidOperationException("not set for unwrapping");
+ }
+
+ int blockSize = engine.GetBlockSize();
+
+ if (inLen < 2 * blockSize)
+ {
+ throw new InvalidCipherTextException("input too short");
+ }
+
+ byte[] cekBlock = new byte[inLen];
+ byte[] iv = new byte[blockSize];
+
+ Array.Copy(inBytes, inOff, cekBlock, 0, inLen);
+ Array.Copy(inBytes, inOff, iv, 0, iv.Length);
+
+ engine.Init(false, new ParametersWithIV(param.Parameters, iv));
+
+ for (int i = blockSize; i < cekBlock.Length; i += blockSize)
+ {
+ engine.ProcessBlock(cekBlock, i, cekBlock, i);
+ }
+
+ Array.Copy(cekBlock, cekBlock.Length - iv.Length, iv, 0, iv.Length);
+
+ engine.Init(false, new ParametersWithIV(param.Parameters, iv));
+
+ engine.ProcessBlock(cekBlock, 0, cekBlock, 0);
+
+ engine.Init(false, param);
+
+ for (int i = 0; i < cekBlock.Length; i += blockSize)
+ {
+ engine.ProcessBlock(cekBlock, i, cekBlock, i);
+ }
+
+ if ((cekBlock[0] & 0xff) > cekBlock.Length - 4)
+ {
+ throw new InvalidCipherTextException("wrapped key corrupted");
+ }
+
+ byte[] key = new byte[cekBlock[0] & 0xff];
+
+ Array.Copy(cekBlock, 4, key, 0, cekBlock[0]);
+
+ // Note: Using constant time comparison
+ int nonEqual = 0;
+ for (int i = 0; i != 3; i++)
+ {
+ byte check = (byte)~cekBlock[1 + i];
+ nonEqual |= (check ^ key[i]);
+ }
+
+ if (nonEqual != 0)
+ throw new InvalidCipherTextException("wrapped key fails checksum");
+
+ return key;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/RFC3394WrapEngine.cs b/Crypto/src/crypto/engines/RFC3394WrapEngine.cs
new file mode 100644
index 000000000..7596e7218
--- /dev/null
+++ b/Crypto/src/crypto/engines/RFC3394WrapEngine.cs
@@ -0,0 +1,178 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /// <remarks>
+ /// An implementation of the AES Key Wrapper from the NIST Key Wrap
+ /// Specification as described in RFC 3394.
+ /// <p/>
+ /// For further details see: <a href="http://www.ietf.org/rfc/rfc3394.txt">http://www.ietf.org/rfc/rfc3394.txt</a>
+ /// and <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
+ /// </remarks>
+ public class Rfc3394WrapEngine
+ : IWrapper
+ {
+ private readonly IBlockCipher engine;
+
+ private KeyParameter param;
+ private bool forWrapping;
+
+ private byte[] iv =
+ {
+ 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6
+ };
+
+ public Rfc3394WrapEngine(
+ IBlockCipher engine)
+ {
+ this.engine = engine;
+ }
+
+ public void Init(
+ bool forWrapping,
+ ICipherParameters parameters)
+ {
+ this.forWrapping = forWrapping;
+
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom) parameters).Parameters;
+ }
+
+ if (parameters is KeyParameter)
+ {
+ this.param = (KeyParameter) parameters;
+ }
+ else if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV pIV = (ParametersWithIV) parameters;
+ byte[] iv = pIV.GetIV();
+
+ if (iv.Length != 8)
+ throw new ArgumentException("IV length not equal to 8", "parameters");
+
+ this.iv = iv;
+ this.param = (KeyParameter) pIV.Parameters;
+ }
+ else
+ {
+ // TODO Throw an exception for bad parameters?
+ }
+ }
+
+ public string AlgorithmName
+ {
+ get { return engine.AlgorithmName; }
+ }
+
+ public byte[] Wrap(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new InvalidOperationException("not set for wrapping");
+ }
+
+ int n = inLen / 8;
+
+ if ((n * 8) != inLen)
+ {
+ throw new DataLengthException("wrap data must be a multiple of 8 bytes");
+ }
+
+ byte[] block = new byte[inLen + iv.Length];
+ byte[] buf = new byte[8 + iv.Length];
+
+ Array.Copy(iv, 0, block, 0, iv.Length);
+ Array.Copy(input, 0, block, iv.Length, inLen);
+
+ engine.Init(true, param);
+
+ for (int j = 0; j != 6; j++)
+ {
+ for (int i = 1; i <= n; i++)
+ {
+ Array.Copy(block, 0, buf, 0, iv.Length);
+ Array.Copy(block, 8 * i, buf, iv.Length, 8);
+ engine.ProcessBlock(buf, 0, buf, 0);
+
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
+
+ buf[iv.Length - k] ^= v;
+ t = (int) ((uint)t >> 8);
+ }
+
+ Array.Copy(buf, 0, block, 0, 8);
+ Array.Copy(buf, 8, block, 8 * i, 8);
+ }
+ }
+
+ return block;
+ }
+
+ public byte[] Unwrap(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ if (forWrapping)
+ {
+ throw new InvalidOperationException("not set for unwrapping");
+ }
+
+ int n = inLen / 8;
+
+ if ((n * 8) != inLen)
+ {
+ throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
+ }
+
+ byte[] block = new byte[inLen - iv.Length];
+ byte[] a = new byte[iv.Length];
+ byte[] buf = new byte[8 + iv.Length];
+
+ Array.Copy(input, 0, a, 0, iv.Length);
+ Array.Copy(input, iv.Length, block, 0, inLen - iv.Length);
+
+ engine.Init(false, param);
+
+ n = n - 1;
+
+ for (int j = 5; j >= 0; j--)
+ {
+ for (int i = n; i >= 1; i--)
+ {
+ Array.Copy(a, 0, buf, 0, iv.Length);
+ Array.Copy(block, 8 * (i - 1), buf, iv.Length, 8);
+
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
+
+ buf[iv.Length - k] ^= v;
+ t = (int) ((uint)t >> 8);
+ }
+
+ engine.ProcessBlock(buf, 0, buf, 0);
+ Array.Copy(buf, 0, a, 0, 8);
+ Array.Copy(buf, 8, block, 8 * (i - 1), 8);
+ }
+ }
+
+ if (!Arrays.ConstantTimeAreEqual(a, iv))
+ throw new InvalidCipherTextException("checksum failed");
+
+ return block;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/RSABlindedEngine.cs b/Crypto/src/crypto/engines/RSABlindedEngine.cs
new file mode 100644
index 000000000..cdf69ddda
--- /dev/null
+++ b/Crypto/src/crypto/engines/RSABlindedEngine.cs
@@ -0,0 +1,124 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * this does your basic RSA algorithm with blinding
+ */
+ public class RsaBlindedEngine
+ : IAsymmetricBlockCipher
+ {
+ private readonly RsaCoreEngine core = new RsaCoreEngine();
+ private RsaKeyParameters key;
+ private SecureRandom random;
+
+ public string AlgorithmName
+ {
+ get { return "RSA"; }
+ }
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters param)
+ {
+ core.Init(forEncryption, param);
+
+ if (param is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (RsaKeyParameters)rParam.Parameters;
+ random = rParam.Random;
+ }
+ else
+ {
+ key = (RsaKeyParameters)param;
+ random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int GetInputBlockSize()
+ {
+ return core.GetInputBlockSize();
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int GetOutputBlockSize()
+ {
+ return core.GetOutputBlockSize();
+ }
+
+ /**
+ * Process a single block using the basic RSA algorithm.
+ *
+ * @param inBuf the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] ProcessBlock(
+ byte[] inBuf,
+ int inOff,
+ int inLen)
+ {
+ if (key == null)
+ throw new InvalidOperationException("RSA engine not initialised");
+
+ BigInteger input = core.ConvertInput(inBuf, inOff, inLen);
+
+ BigInteger result;
+ if (key is RsaPrivateCrtKeyParameters)
+ {
+ RsaPrivateCrtKeyParameters k = (RsaPrivateCrtKeyParameters)key;
+ BigInteger e = k.PublicExponent;
+ if (e != null) // can't do blinding without a public exponent
+ {
+ BigInteger m = k.Modulus;
+ BigInteger r = BigIntegers.CreateRandomInRange(
+ BigInteger.One, m.Subtract(BigInteger.One), random);
+
+ BigInteger blindedInput = r.ModPow(e, m).Multiply(input).Mod(m);
+ BigInteger blindedResult = core.ProcessBlock(blindedInput);
+
+ BigInteger rInv = r.ModInverse(m);
+ result = blindedResult.Multiply(rInv).Mod(m);
+ }
+ else
+ {
+ result = core.ProcessBlock(input);
+ }
+ }
+ else
+ {
+ result = core.ProcessBlock(input);
+ }
+
+ return core.ConvertOutput(result);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/RSABlindingEngine.cs b/Crypto/src/crypto/engines/RSABlindingEngine.cs
new file mode 100644
index 000000000..76b57a3f7
--- /dev/null
+++ b/Crypto/src/crypto/engines/RSABlindingEngine.cs
@@ -0,0 +1,139 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * This does your basic RSA Chaum's blinding and unblinding as outlined in
+ * "Handbook of Applied Cryptography", page 475. You need to use this if you are
+ * trying to get another party to generate signatures without them being aware
+ * of the message they are signing.
+ */
+ public class RsaBlindingEngine
+ : IAsymmetricBlockCipher
+ {
+ private readonly RsaCoreEngine core = new RsaCoreEngine();
+
+ private RsaKeyParameters key;
+ private BigInteger blindingFactor;
+
+ private bool forEncryption;
+
+ public string AlgorithmName
+ {
+ get { return "RSA"; }
+ }
+
+ /**
+ * Initialise the blinding engine.
+ *
+ * @param forEncryption true if we are encrypting (blinding), false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters param)
+ {
+ RsaBlindingParameters p;
+
+ if (param is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ p = (RsaBlindingParameters)rParam.Parameters;
+ }
+ else
+ {
+ p = (RsaBlindingParameters)param;
+ }
+
+ core.Init(forEncryption, p.PublicKey);
+
+ this.forEncryption = forEncryption;
+ this.key = p.PublicKey;
+ this.blindingFactor = p.BlindingFactor;
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int GetInputBlockSize()
+ {
+ return core.GetInputBlockSize();
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int GetOutputBlockSize()
+ {
+ return core.GetOutputBlockSize();
+ }
+
+ /**
+ * Process a single block using the RSA blinding algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @throws DataLengthException the input block is too large.
+ */
+ public byte[] ProcessBlock(
+ byte[] inBuf,
+ int inOff,
+ int inLen)
+ {
+ BigInteger msg = core.ConvertInput(inBuf, inOff, inLen);
+
+ if (forEncryption)
+ {
+ msg = BlindMessage(msg);
+ }
+ else
+ {
+ msg = UnblindMessage(msg);
+ }
+
+ return core.ConvertOutput(msg);
+ }
+
+ /*
+ * Blind message with the blind factor.
+ */
+ private BigInteger BlindMessage(
+ BigInteger msg)
+ {
+ BigInteger blindMsg = blindingFactor;
+ blindMsg = msg.Multiply(blindMsg.ModPow(key.Exponent, key.Modulus));
+ blindMsg = blindMsg.Mod(key.Modulus);
+
+ return blindMsg;
+ }
+
+ /*
+ * Unblind the message blinded with the blind factor.
+ */
+ private BigInteger UnblindMessage(
+ BigInteger blindedMsg)
+ {
+ BigInteger m = key.Modulus;
+ BigInteger msg = blindedMsg;
+ BigInteger blindFactorInverse = blindingFactor.ModInverse(m);
+ msg = msg.Multiply(blindFactorInverse);
+ msg = msg.Mod(m);
+
+ return msg;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/RSACoreEngine.cs b/Crypto/src/crypto/engines/RSACoreEngine.cs
new file mode 100644
index 000000000..4e64d25d6
--- /dev/null
+++ b/Crypto/src/crypto/engines/RSACoreEngine.cs
@@ -0,0 +1,156 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * this does your basic RSA algorithm.
+ */
+ class RsaCoreEngine
+ {
+ private RsaKeyParameters key;
+ private bool forEncryption;
+ private int bitSize;
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ parameters = ((ParametersWithRandom) parameters).Parameters;
+ }
+
+ if (!(parameters is RsaKeyParameters))
+ throw new InvalidKeyException("Not an RSA key");
+
+ this.key = (RsaKeyParameters) parameters;
+ this.forEncryption = forEncryption;
+ this.bitSize = key.Modulus.BitLength;
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int GetInputBlockSize()
+ {
+ if (forEncryption)
+ {
+ return (bitSize - 1) / 8;
+ }
+
+ return (bitSize + 7) / 8;
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int GetOutputBlockSize()
+ {
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8;
+ }
+
+ return (bitSize - 1) / 8;
+ }
+
+ public BigInteger ConvertInput(
+ byte[] inBuf,
+ int inOff,
+ int inLen)
+ {
+ int maxLength = (bitSize + 7) / 8;
+
+ if (inLen > maxLength)
+ throw new DataLengthException("input too large for RSA cipher.");
+
+ BigInteger input = new BigInteger(1, inBuf, inOff, inLen);
+
+ if (input.CompareTo(key.Modulus) >= 0)
+ throw new DataLengthException("input too large for RSA cipher.");
+
+ return input;
+ }
+
+ public byte[] ConvertOutput(
+ BigInteger result)
+ {
+ byte[] output = result.ToByteArrayUnsigned();
+
+ if (forEncryption)
+ {
+ int outSize = GetOutputBlockSize();
+
+ // TODO To avoid this, create version of BigInteger.ToByteArray that
+ // writes to an existing array
+ if (output.Length < outSize) // have ended up with less bytes than normal, lengthen
+ {
+ byte[] tmp = new byte[outSize];
+ output.CopyTo(tmp, tmp.Length - output.Length);
+ output = tmp;
+ }
+ }
+
+ return output;
+ }
+
+ public BigInteger ProcessBlock(
+ BigInteger input)
+ {
+ if (key is RsaPrivateCrtKeyParameters)
+ {
+ //
+ // we have the extra factors, use the Chinese Remainder Theorem - the author
+ // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for
+ // advice regarding the expression of this.
+ //
+ RsaPrivateCrtKeyParameters crtKey = (RsaPrivateCrtKeyParameters)key;
+
+ BigInteger p = crtKey.P;;
+ BigInteger q = crtKey.Q;
+ BigInteger dP = crtKey.DP;
+ BigInteger dQ = crtKey.DQ;
+ BigInteger qInv = crtKey.QInv;
+
+ BigInteger mP, mQ, h, m;
+
+ // mP = ((input Mod p) ^ dP)) Mod p
+ mP = (input.Remainder(p)).ModPow(dP, p);
+
+ // mQ = ((input Mod q) ^ dQ)) Mod q
+ mQ = (input.Remainder(q)).ModPow(dQ, q);
+
+ // h = qInv * (mP - mQ) Mod p
+ h = mP.Subtract(mQ);
+ h = h.Multiply(qInv);
+ h = h.Mod(p); // Mod (in Java) returns the positive residual
+
+ // m = h * q + mQ
+ m = h.Multiply(q);
+ m = m.Add(mQ);
+
+ return m;
+ }
+
+ return input.ModPow(key.Exponent, key.Modulus);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/RijndaelEngine.cs b/Crypto/src/crypto/engines/RijndaelEngine.cs
new file mode 100644
index 000000000..df2e5baea
--- /dev/null
+++ b/Crypto/src/crypto/engines/RijndaelEngine.cs
@@ -0,0 +1,747 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * an implementation of Rijndael, based on the documentation and reference implementation
+ * by Paulo Barreto, Vincent Rijmen, for v2.0 August '99.
+ * <p>
+ * Note: this implementation is based on information prior to readonly NIST publication.
+ * </p>
+ */
+ public class RijndaelEngine
+ : IBlockCipher
+ {
+ private static readonly int MAXROUNDS = 14;
+
+ private static readonly int MAXKC = (256/4);
+
+ private static readonly byte[] Logtable =
+ {
+ 0, 0, 25, 1, 50, 2, 26, 198,
+ 75, 199, 27, 104, 51, 238, 223, 3,
+ 100, 4, 224, 14, 52, 141, 129, 239,
+ 76, 113, 8, 200, 248, 105, 28, 193,
+ 125, 194, 29, 181, 249, 185, 39, 106,
+ 77, 228, 166, 114, 154, 201, 9, 120,
+ 101, 47, 138, 5, 33, 15, 225, 36,
+ 18, 240, 130, 69, 53, 147, 218, 142,
+ 150, 143, 219, 189, 54, 208, 206, 148,
+ 19, 92, 210, 241, 64, 70, 131, 56,
+ 102, 221, 253, 48, 191, 6, 139, 98,
+ 179, 37, 226, 152, 34, 136, 145, 16,
+ 126, 110, 72, 195, 163, 182, 30, 66,
+ 58, 107, 40, 84, 250, 133, 61, 186,
+ 43, 121, 10, 21, 155, 159, 94, 202,
+ 78, 212, 172, 229, 243, 115, 167, 87,
+ 175, 88, 168, 80, 244, 234, 214, 116,
+ 79, 174, 233, 213, 231, 230, 173, 232,
+ 44, 215, 117, 122, 235, 22, 11, 245,
+ 89, 203, 95, 176, 156, 169, 81, 160,
+ 127, 12, 246, 111, 23, 196, 73, 236,
+ 216, 67, 31, 45, 164, 118, 123, 183,
+ 204, 187, 62, 90, 251, 96, 177, 134,
+ 59, 82, 161, 108, 170, 85, 41, 157,
+ 151, 178, 135, 144, 97, 190, 220, 252,
+ 188, 149, 207, 205, 55, 63, 91, 209,
+ 83, 57, 132, 60, 65, 162, 109, 71,
+ 20, 42, 158, 93, 86, 242, 211, 171,
+ 68, 17, 146, 217, 35, 32, 46, 137,
+ 180, 124, 184, 38, 119, 153, 227, 165,
+ 103, 74, 237, 222, 197, 49, 254, 24,
+ 13, 99, 140, 128, 192, 247, 112, 7
+ };
+
+ private static readonly byte[] Alogtable =
+ {
+ 0, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53,
+ 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170,
+ 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49,
+ 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205,
+ 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136,
+ 131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154,
+ 181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163,
+ 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160,
+ 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65,
+ 195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117,
+ 159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128,
+ 155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84,
+ 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202,
+ 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14,
+ 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23,
+ 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1,
+ 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53,
+ 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170,
+ 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49,
+ 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205,
+ 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136,
+ 131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154,
+ 181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163,
+ 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160,
+ 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65,
+ 195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117,
+ 159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128,
+ 155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84,
+ 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202,
+ 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14,
+ 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23,
+ 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1,
+ };
+
+ private static readonly byte[] S =
+ {
+ 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118,
+ 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192,
+ 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21,
+ 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117,
+ 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132,
+ 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207,
+ 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168,
+ 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210,
+ 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115,
+ 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219,
+ 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121,
+ 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8,
+ 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138,
+ 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158,
+ 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
+ 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22,
+ };
+
+ private static readonly byte[] Si =
+ {
+ 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251,
+ 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203,
+ 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78,
+ 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37,
+ 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146,
+ 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132,
+ 144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6,
+ 208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107,
+ 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115,
+ 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110,
+ 71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27,
+ 252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244,
+ 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95,
+ 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239,
+ 160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97,
+ 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125,
+ };
+
+ private static readonly byte[] rcon =
+ {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91
+ };
+
+ static readonly byte[][] shifts0 = new byte [][]
+ {
+ new byte[]{ 0, 8, 16, 24 },
+ new byte[]{ 0, 8, 16, 24 },
+ new byte[]{ 0, 8, 16, 24 },
+ new byte[]{ 0, 8, 16, 32 },
+ new byte[]{ 0, 8, 24, 32 }
+ };
+
+ static readonly byte[][] shifts1 =
+ {
+ new byte[]{ 0, 24, 16, 8 },
+ new byte[]{ 0, 32, 24, 16 },
+ new byte[]{ 0, 40, 32, 24 },
+ new byte[]{ 0, 48, 40, 24 },
+ new byte[]{ 0, 56, 40, 32 }
+ };
+
+ /**
+ * multiply two elements of GF(2^m)
+ * needed for MixColumn and InvMixColumn
+ */
+ private byte Mul0x2(
+ int b)
+ {
+ if (b != 0)
+ {
+ return Alogtable[25 + (Logtable[b] & 0xff)];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte Mul0x3(
+ int b)
+ {
+ if (b != 0)
+ {
+ return Alogtable[1 + (Logtable[b] & 0xff)];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte Mul0x9(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return Alogtable[199 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte Mul0xb(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return Alogtable[104 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte Mul0xd(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return Alogtable[238 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte Mul0xe(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return Alogtable[223 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ /**
+ * xor corresponding text input and round key input bytes
+ */
+ private void KeyAddition(
+ long[] rk)
+ {
+ A0 ^= rk[0];
+ A1 ^= rk[1];
+ A2 ^= rk[2];
+ A3 ^= rk[3];
+ }
+
+ private long Shift(
+ long r,
+ int shift)
+ {
+ //return (((long)((ulong) r >> shift) | (r << (BC - shift)))) & BC_MASK;
+
+ ulong temp = (ulong) r >> shift;
+
+ // NB: This corrects for Mono Bug #79087 (fixed in 1.1.17)
+ if (shift > 31)
+ {
+ temp &= 0xFFFFFFFFUL;
+ }
+
+ return ((long) temp | (r << (BC - shift))) & BC_MASK;
+ }
+
+ /**
+ * Row 0 remains unchanged
+ * The other three rows are shifted a variable amount
+ */
+ private void ShiftRow(
+ byte[] shiftsSC)
+ {
+ A1 = Shift(A1, shiftsSC[1]);
+ A2 = Shift(A2, shiftsSC[2]);
+ A3 = Shift(A3, shiftsSC[3]);
+ }
+
+ private long ApplyS(
+ long r,
+ byte[] box)
+ {
+ long res = 0;
+
+ for (int j = 0; j < BC; j += 8)
+ {
+ res |= (long)(box[(int)((r >> j) & 0xff)] & 0xff) << j;
+ }
+
+ return res;
+ }
+
+ /**
+ * Replace every byte of the input by the byte at that place
+ * in the nonlinear S-box
+ */
+ private void Substitution(
+ byte[] box)
+ {
+ A0 = ApplyS(A0, box);
+ A1 = ApplyS(A1, box);
+ A2 = ApplyS(A2, box);
+ A3 = ApplyS(A3, box);
+ }
+
+ /**
+ * Mix the bytes of every column in a linear way
+ */
+ private void MixColumn()
+ {
+ long r0, r1, r2, r3;
+
+ r0 = r1 = r2 = r3 = 0;
+
+ for (int j = 0; j < BC; j += 8)
+ {
+ int a0 = (int)((A0 >> j) & 0xff);
+ int a1 = (int)((A1 >> j) & 0xff);
+ int a2 = (int)((A2 >> j) & 0xff);
+ int a3 = (int)((A3 >> j) & 0xff);
+
+ r0 |= (long)((Mul0x2(a0) ^ Mul0x3(a1) ^ a2 ^ a3) & 0xff) << j;
+
+ r1 |= (long)((Mul0x2(a1) ^ Mul0x3(a2) ^ a3 ^ a0) & 0xff) << j;
+
+ r2 |= (long)((Mul0x2(a2) ^ Mul0x3(a3) ^ a0 ^ a1) & 0xff) << j;
+
+ r3 |= (long)((Mul0x2(a3) ^ Mul0x3(a0) ^ a1 ^ a2) & 0xff) << j;
+ }
+
+ A0 = r0;
+ A1 = r1;
+ A2 = r2;
+ A3 = r3;
+ }
+
+ /**
+ * Mix the bytes of every column in a linear way
+ * This is the opposite operation of Mixcolumn
+ */
+ private void InvMixColumn()
+ {
+ long r0, r1, r2, r3;
+
+ r0 = r1 = r2 = r3 = 0;
+ for (int j = 0; j < BC; j += 8)
+ {
+ int a0 = (int)((A0 >> j) & 0xff);
+ int a1 = (int)((A1 >> j) & 0xff);
+ int a2 = (int)((A2 >> j) & 0xff);
+ int a3 = (int)((A3 >> j) & 0xff);
+
+ //
+ // pre-lookup the log table
+ //
+ a0 = (a0 != 0) ? (Logtable[a0 & 0xff] & 0xff) : -1;
+ a1 = (a1 != 0) ? (Logtable[a1 & 0xff] & 0xff) : -1;
+ a2 = (a2 != 0) ? (Logtable[a2 & 0xff] & 0xff) : -1;
+ a3 = (a3 != 0) ? (Logtable[a3 & 0xff] & 0xff) : -1;
+
+ r0 |= (long)((Mul0xe(a0) ^ Mul0xb(a1) ^ Mul0xd(a2) ^ Mul0x9(a3)) & 0xff) << j;
+
+ r1 |= (long)((Mul0xe(a1) ^ Mul0xb(a2) ^ Mul0xd(a3) ^ Mul0x9(a0)) & 0xff) << j;
+
+ r2 |= (long)((Mul0xe(a2) ^ Mul0xb(a3) ^ Mul0xd(a0) ^ Mul0x9(a1)) & 0xff) << j;
+
+ r3 |= (long)((Mul0xe(a3) ^ Mul0xb(a0) ^ Mul0xd(a1) ^ Mul0x9(a2)) & 0xff) << j;
+ }
+
+ A0 = r0;
+ A1 = r1;
+ A2 = r2;
+ A3 = r3;
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on keyBits and blockBits
+ */
+ private long[][] GenerateWorkingKey(
+ byte[] key)
+ {
+ int KC;
+ int t, rconpointer = 0;
+ int keyBits = key.Length * 8;
+ byte[,] tk = new byte[4,MAXKC];
+ //long[,] W = new long[MAXROUNDS+1,4];
+ long[][] W = new long[MAXROUNDS+1][];
+
+ for (int i = 0; i < MAXROUNDS+1; i++) W[i] = new long[4];
+
+ switch (keyBits)
+ {
+ case 128:
+ KC = 4;
+ break;
+ case 160:
+ KC = 5;
+ break;
+ case 192:
+ KC = 6;
+ break;
+ case 224:
+ KC = 7;
+ break;
+ case 256:
+ KC = 8;
+ break;
+ default :
+ throw new ArgumentException("Key length not 128/160/192/224/256 bits.");
+ }
+
+ if (keyBits >= blockBits)
+ {
+ ROUNDS = KC + 6;
+ }
+ else
+ {
+ ROUNDS = (BC / 8) + 6;
+ }
+
+ //
+ // copy the key into the processing area
+ //
+ int index = 0;
+
+ for (int i = 0; i < key.Length; i++)
+ {
+ tk[i % 4,i / 4] = key[index++];
+ }
+
+ t = 0;
+
+ //
+ // copy values into round key array
+ //
+ for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC / 8)); j++, t++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[t / (BC / 8)][i] |= (long)(tk[i,j] & 0xff) << ((t * 8) % BC);
+ }
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ while (t < (ROUNDS+1)*(BC/8))
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i,0] ^= S[tk[(i+1)%4,KC-1] & 0xff];
+ }
+ tk[0,0] ^= (byte) rcon[rconpointer++];
+
+ if (KC <= 6)
+ {
+ for (int j = 1; j < KC; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i,j] ^= tk[i,j-1];
+ }
+ }
+ }
+ else
+ {
+ for (int j = 1; j < 4; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i,j] ^= tk[i,j-1];
+ }
+ }
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i,4] ^= S[tk[i,3] & 0xff];
+ }
+ for (int j = 5; j < KC; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i,j] ^= tk[i,j-1];
+ }
+ }
+ }
+
+ //
+ // copy values into round key array
+ //
+ for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC/8)); j++, t++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[t / (BC/8)][i] |= (long)(tk[i,j] & 0xff) << ((t * 8) % (BC));
+ }
+ }
+ }
+ return W;
+ }
+
+ private int BC;
+ private long BC_MASK;
+ private int ROUNDS;
+ private int blockBits;
+ private long[][] workingKey;
+ private long A0, A1, A2, A3;
+ private bool forEncryption;
+ private byte[] shifts0SC;
+ private byte[] shifts1SC;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public RijndaelEngine() : this(128) {}
+
+ /**
+ * basic constructor - set the cipher up for a given blocksize
+ *
+ * @param blocksize the blocksize in bits, must be 128, 192, or 256.
+ */
+ public RijndaelEngine(
+ int blockBits)
+ {
+ switch (blockBits)
+ {
+ case 128:
+ BC = 32;
+ BC_MASK = 0xffffffffL;
+ shifts0SC = shifts0[0];
+ shifts1SC = shifts1[0];
+ break;
+ case 160:
+ BC = 40;
+ BC_MASK = 0xffffffffffL;
+ shifts0SC = shifts0[1];
+ shifts1SC = shifts1[1];
+ break;
+ case 192:
+ BC = 48;
+ BC_MASK = 0xffffffffffffL;
+ shifts0SC = shifts0[2];
+ shifts1SC = shifts1[2];
+ break;
+ case 224:
+ BC = 56;
+ BC_MASK = 0xffffffffffffffL;
+ shifts0SC = shifts0[3];
+ shifts1SC = shifts1[3];
+ break;
+ case 256:
+ BC = 64;
+ BC_MASK = unchecked( (long)0xffffffffffffffffL);
+ shifts0SC = shifts0[4];
+ shifts1SC = shifts1[4];
+ break;
+ default:
+ throw new ArgumentException("unknown blocksize to Rijndael");
+ }
+
+ this.blockBits = blockBits;
+ }
+
+ /**
+ * initialise a Rijndael cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (typeof(KeyParameter).IsInstanceOfType(parameters))
+ {
+ workingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey());
+ this.forEncryption = forEncryption;
+ return;
+ }
+
+ throw new ArgumentException("invalid parameter passed to Rijndael init - " + parameters.GetType().ToString());
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Rijndael"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BC / 2;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new InvalidOperationException("Rijndael engine not initialised");
+ }
+
+ if ((inOff + (BC / 2)) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (BC / 2)) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ UnPackBlock(input, inOff);
+
+ if (forEncryption)
+ {
+ EncryptBlock(workingKey);
+ }
+ else
+ {
+ DecryptBlock(workingKey);
+ }
+
+ PackBlock(output, outOff);
+
+ return BC / 2;
+ }
+
+ public void Reset()
+ {
+ }
+
+ private void UnPackBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ A0 = (long)(bytes[index++] & 0xff);
+ A1 = (long)(bytes[index++] & 0xff);
+ A2 = (long)(bytes[index++] & 0xff);
+ A3 = (long)(bytes[index++] & 0xff);
+
+ for (int j = 8; j != BC; j += 8)
+ {
+ A0 |= (long)(bytes[index++] & 0xff) << j;
+ A1 |= (long)(bytes[index++] & 0xff) << j;
+ A2 |= (long)(bytes[index++] & 0xff) << j;
+ A3 |= (long)(bytes[index++] & 0xff) << j;
+ }
+ }
+
+ private void PackBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ for (int j = 0; j != BC; j += 8)
+ {
+ bytes[index++] = (byte)(A0 >> j);
+ bytes[index++] = (byte)(A1 >> j);
+ bytes[index++] = (byte)(A2 >> j);
+ bytes[index++] = (byte)(A3 >> j);
+ }
+ }
+
+ private void EncryptBlock(
+ long[][] rk)
+ {
+ int r;
+
+ //
+ // begin with a key addition
+ //
+ KeyAddition(rk[0]);
+
+ //
+ // ROUNDS-1 ordinary rounds
+ //
+ for (r = 1; r < ROUNDS; r++)
+ {
+ Substitution(S);
+ ShiftRow(shifts0SC);
+ MixColumn();
+ KeyAddition(rk[r]);
+ }
+
+ //
+ // Last round is special: there is no MixColumn
+ //
+ Substitution(S);
+ ShiftRow(shifts0SC);
+ KeyAddition(rk[ROUNDS]);
+ }
+
+ private void DecryptBlock(
+ long[][] rk)
+ {
+ int r;
+
+ // To decrypt: apply the inverse operations of the encrypt routine,
+ // in opposite order
+ //
+ // (KeyAddition is an involution: it 's equal to its inverse)
+ // (the inverse of Substitution with table S is Substitution with the inverse table of S)
+ // (the inverse of Shiftrow is Shiftrow over a suitable distance)
+ //
+
+ // First the special round:
+ // without InvMixColumn
+ // with extra KeyAddition
+ //
+ KeyAddition(rk[ROUNDS]);
+ Substitution(Si);
+ ShiftRow(shifts1SC);
+
+ //
+ // ROUNDS-1 ordinary rounds
+ //
+ for (r = ROUNDS-1; r > 0; r--)
+ {
+ KeyAddition(rk[r]);
+ InvMixColumn();
+ Substitution(Si);
+ ShiftRow(shifts1SC);
+ }
+
+ //
+ // End with the extra key addition
+ //
+ KeyAddition(rk[0]);
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/RsaEngine.cs b/Crypto/src/crypto/engines/RsaEngine.cs
new file mode 100644
index 000000000..7e6dfb163
--- /dev/null
+++ b/Crypto/src/crypto/engines/RsaEngine.cs
@@ -0,0 +1,78 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * this does your basic RSA algorithm.
+ */
+ public class RsaEngine
+ : IAsymmetricBlockCipher
+ {
+ private RsaCoreEngine core;
+
+ public string AlgorithmName
+ {
+ get { return "RSA"; }
+ }
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (core == null)
+ core = new RsaCoreEngine();
+
+ core.Init(forEncryption, parameters);
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int GetInputBlockSize()
+ {
+ return core.GetInputBlockSize();
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int GetOutputBlockSize()
+ {
+ return core.GetOutputBlockSize();
+ }
+
+ /**
+ * Process a single block using the basic RSA algorithm.
+ *
+ * @param inBuf the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] ProcessBlock(
+ byte[] inBuf,
+ int inOff,
+ int inLen)
+ {
+ if (core == null)
+ throw new InvalidOperationException("RSA engine not initialised");
+
+ return core.ConvertOutput(core.ProcessBlock(core.ConvertInput(inBuf, inOff, inLen)));
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/SEEDEngine.cs b/Crypto/src/crypto/engines/SEEDEngine.cs
new file mode 100644
index 000000000..efea0f1fe
--- /dev/null
+++ b/Crypto/src/crypto/engines/SEEDEngine.cs
@@ -0,0 +1,361 @@
+using System;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * Implementation of the SEED algorithm as described in RFC 4009
+ */
+ public class SeedEngine
+ : IBlockCipher
+ {
+ private const int BlockSize = 16;
+
+ private static readonly uint[] SS0 =
+ {
+ 0x2989a1a8, 0x05858184, 0x16c6d2d4, 0x13c3d3d0, 0x14445054, 0x1d0d111c, 0x2c8ca0ac, 0x25052124,
+ 0x1d4d515c, 0x03434340, 0x18081018, 0x1e0e121c, 0x11415150, 0x3cccf0fc, 0x0acac2c8, 0x23436360,
+ 0x28082028, 0x04444044, 0x20002020, 0x1d8d919c, 0x20c0e0e0, 0x22c2e2e0, 0x08c8c0c8, 0x17071314,
+ 0x2585a1a4, 0x0f8f838c, 0x03030300, 0x3b4b7378, 0x3b8bb3b8, 0x13031310, 0x12c2d2d0, 0x2ecee2ec,
+ 0x30407070, 0x0c8c808c, 0x3f0f333c, 0x2888a0a8, 0x32023230, 0x1dcdd1dc, 0x36c6f2f4, 0x34447074,
+ 0x2ccce0ec, 0x15859194, 0x0b0b0308, 0x17475354, 0x1c4c505c, 0x1b4b5358, 0x3d8db1bc, 0x01010100,
+ 0x24042024, 0x1c0c101c, 0x33437370, 0x18889098, 0x10001010, 0x0cccc0cc, 0x32c2f2f0, 0x19c9d1d8,
+ 0x2c0c202c, 0x27c7e3e4, 0x32427270, 0x03838380, 0x1b8b9398, 0x11c1d1d0, 0x06868284, 0x09c9c1c8,
+ 0x20406060, 0x10405050, 0x2383a3a0, 0x2bcbe3e8, 0x0d0d010c, 0x3686b2b4, 0x1e8e929c, 0x0f4f434c,
+ 0x3787b3b4, 0x1a4a5258, 0x06c6c2c4, 0x38487078, 0x2686a2a4, 0x12021210, 0x2f8fa3ac, 0x15c5d1d4,
+ 0x21416160, 0x03c3c3c0, 0x3484b0b4, 0x01414140, 0x12425250, 0x3d4d717c, 0x0d8d818c, 0x08080008,
+ 0x1f0f131c, 0x19899198, 0x00000000, 0x19091118, 0x04040004, 0x13435350, 0x37c7f3f4, 0x21c1e1e0,
+ 0x3dcdf1fc, 0x36467274, 0x2f0f232c, 0x27072324, 0x3080b0b0, 0x0b8b8388, 0x0e0e020c, 0x2b8ba3a8,
+ 0x2282a2a0, 0x2e4e626c, 0x13839390, 0x0d4d414c, 0x29496168, 0x3c4c707c, 0x09090108, 0x0a0a0208,
+ 0x3f8fb3bc, 0x2fcfe3ec, 0x33c3f3f0, 0x05c5c1c4, 0x07878384, 0x14041014, 0x3ecef2fc, 0x24446064,
+ 0x1eced2dc, 0x2e0e222c, 0x0b4b4348, 0x1a0a1218, 0x06060204, 0x21012120, 0x2b4b6368, 0x26466264,
+ 0x02020200, 0x35c5f1f4, 0x12829290, 0x0a8a8288, 0x0c0c000c, 0x3383b3b0, 0x3e4e727c, 0x10c0d0d0,
+ 0x3a4a7278, 0x07474344, 0x16869294, 0x25c5e1e4, 0x26062224, 0x00808080, 0x2d8da1ac, 0x1fcfd3dc,
+ 0x2181a1a0, 0x30003030, 0x37073334, 0x2e8ea2ac, 0x36063234, 0x15051114, 0x22022220, 0x38083038,
+ 0x34c4f0f4, 0x2787a3a4, 0x05454144, 0x0c4c404c, 0x01818180, 0x29c9e1e8, 0x04848084, 0x17879394,
+ 0x35053134, 0x0bcbc3c8, 0x0ecec2cc, 0x3c0c303c, 0x31417170, 0x11011110, 0x07c7c3c4, 0x09898188,
+ 0x35457174, 0x3bcbf3f8, 0x1acad2d8, 0x38c8f0f8, 0x14849094, 0x19495158, 0x02828280, 0x04c4c0c4,
+ 0x3fcff3fc, 0x09494148, 0x39093138, 0x27476364, 0x00c0c0c0, 0x0fcfc3cc, 0x17c7d3d4, 0x3888b0b8,
+ 0x0f0f030c, 0x0e8e828c, 0x02424240, 0x23032320, 0x11819190, 0x2c4c606c, 0x1bcbd3d8, 0x2484a0a4,
+ 0x34043034, 0x31c1f1f0, 0x08484048, 0x02c2c2c0, 0x2f4f636c, 0x3d0d313c, 0x2d0d212c, 0x00404040,
+ 0x3e8eb2bc, 0x3e0e323c, 0x3c8cb0bc, 0x01c1c1c0, 0x2a8aa2a8, 0x3a8ab2b8, 0x0e4e424c, 0x15455154,
+ 0x3b0b3338, 0x1cccd0dc, 0x28486068, 0x3f4f737c, 0x1c8c909c, 0x18c8d0d8, 0x0a4a4248, 0x16465254,
+ 0x37477374, 0x2080a0a0, 0x2dcde1ec, 0x06464244, 0x3585b1b4, 0x2b0b2328, 0x25456164, 0x3acaf2f8,
+ 0x23c3e3e0, 0x3989b1b8, 0x3181b1b0, 0x1f8f939c, 0x1e4e525c, 0x39c9f1f8, 0x26c6e2e4, 0x3282b2b0,
+ 0x31013130, 0x2acae2e8, 0x2d4d616c, 0x1f4f535c, 0x24c4e0e4, 0x30c0f0f0, 0x0dcdc1cc, 0x08888088,
+ 0x16061214, 0x3a0a3238, 0x18485058, 0x14c4d0d4, 0x22426260, 0x29092128, 0x07070304, 0x33033330,
+ 0x28c8e0e8, 0x1b0b1318, 0x05050104, 0x39497178, 0x10809090, 0x2a4a6268, 0x2a0a2228, 0x1a8a9298
+ };
+
+ private static readonly uint[] SS1 =
+ {
+ 0x38380830, 0xe828c8e0, 0x2c2d0d21, 0xa42686a2, 0xcc0fcfc3, 0xdc1eced2, 0xb03383b3, 0xb83888b0,
+ 0xac2f8fa3, 0x60204060, 0x54154551, 0xc407c7c3, 0x44044440, 0x6c2f4f63, 0x682b4b63, 0x581b4b53,
+ 0xc003c3c3, 0x60224262, 0x30330333, 0xb43585b1, 0x28290921, 0xa02080a0, 0xe022c2e2, 0xa42787a3,
+ 0xd013c3d3, 0x90118191, 0x10110111, 0x04060602, 0x1c1c0c10, 0xbc3c8cb0, 0x34360632, 0x480b4b43,
+ 0xec2fcfe3, 0x88088880, 0x6c2c4c60, 0xa82888a0, 0x14170713, 0xc404c4c0, 0x14160612, 0xf434c4f0,
+ 0xc002c2c2, 0x44054541, 0xe021c1e1, 0xd416c6d2, 0x3c3f0f33, 0x3c3d0d31, 0x8c0e8e82, 0x98188890,
+ 0x28280820, 0x4c0e4e42, 0xf436c6f2, 0x3c3e0e32, 0xa42585a1, 0xf839c9f1, 0x0c0d0d01, 0xdc1fcfd3,
+ 0xd818c8d0, 0x282b0b23, 0x64264662, 0x783a4a72, 0x24270723, 0x2c2f0f23, 0xf031c1f1, 0x70324272,
+ 0x40024242, 0xd414c4d0, 0x40014141, 0xc000c0c0, 0x70334373, 0x64274763, 0xac2c8ca0, 0x880b8b83,
+ 0xf437c7f3, 0xac2d8da1, 0x80008080, 0x1c1f0f13, 0xc80acac2, 0x2c2c0c20, 0xa82a8aa2, 0x34340430,
+ 0xd012c2d2, 0x080b0b03, 0xec2ecee2, 0xe829c9e1, 0x5c1d4d51, 0x94148490, 0x18180810, 0xf838c8f0,
+ 0x54174753, 0xac2e8ea2, 0x08080800, 0xc405c5c1, 0x10130313, 0xcc0dcdc1, 0x84068682, 0xb83989b1,
+ 0xfc3fcff3, 0x7c3d4d71, 0xc001c1c1, 0x30310131, 0xf435c5f1, 0x880a8a82, 0x682a4a62, 0xb03181b1,
+ 0xd011c1d1, 0x20200020, 0xd417c7d3, 0x00020202, 0x20220222, 0x04040400, 0x68284860, 0x70314171,
+ 0x04070703, 0xd81bcbd3, 0x9c1d8d91, 0x98198991, 0x60214161, 0xbc3e8eb2, 0xe426c6e2, 0x58194951,
+ 0xdc1dcdd1, 0x50114151, 0x90108090, 0xdc1cccd0, 0x981a8a92, 0xa02383a3, 0xa82b8ba3, 0xd010c0d0,
+ 0x80018181, 0x0c0f0f03, 0x44074743, 0x181a0a12, 0xe023c3e3, 0xec2ccce0, 0x8c0d8d81, 0xbc3f8fb3,
+ 0x94168692, 0x783b4b73, 0x5c1c4c50, 0xa02282a2, 0xa02181a1, 0x60234363, 0x20230323, 0x4c0d4d41,
+ 0xc808c8c0, 0x9c1e8e92, 0x9c1c8c90, 0x383a0a32, 0x0c0c0c00, 0x2c2e0e22, 0xb83a8ab2, 0x6c2e4e62,
+ 0x9c1f8f93, 0x581a4a52, 0xf032c2f2, 0x90128292, 0xf033c3f3, 0x48094941, 0x78384870, 0xcc0cccc0,
+ 0x14150511, 0xf83bcbf3, 0x70304070, 0x74354571, 0x7c3f4f73, 0x34350531, 0x10100010, 0x00030303,
+ 0x64244460, 0x6c2d4d61, 0xc406c6c2, 0x74344470, 0xd415c5d1, 0xb43484b0, 0xe82acae2, 0x08090901,
+ 0x74364672, 0x18190911, 0xfc3ecef2, 0x40004040, 0x10120212, 0xe020c0e0, 0xbc3d8db1, 0x04050501,
+ 0xf83acaf2, 0x00010101, 0xf030c0f0, 0x282a0a22, 0x5c1e4e52, 0xa82989a1, 0x54164652, 0x40034343,
+ 0x84058581, 0x14140410, 0x88098981, 0x981b8b93, 0xb03080b0, 0xe425c5e1, 0x48084840, 0x78394971,
+ 0x94178793, 0xfc3cccf0, 0x1c1e0e12, 0x80028282, 0x20210121, 0x8c0c8c80, 0x181b0b13, 0x5c1f4f53,
+ 0x74374773, 0x54144450, 0xb03282b2, 0x1c1d0d11, 0x24250521, 0x4c0f4f43, 0x00000000, 0x44064642,
+ 0xec2dcde1, 0x58184850, 0x50124252, 0xe82bcbe3, 0x7c3e4e72, 0xd81acad2, 0xc809c9c1, 0xfc3dcdf1,
+ 0x30300030, 0x94158591, 0x64254561, 0x3c3c0c30, 0xb43686b2, 0xe424c4e0, 0xb83b8bb3, 0x7c3c4c70,
+ 0x0c0e0e02, 0x50104050, 0x38390931, 0x24260622, 0x30320232, 0x84048480, 0x68294961, 0x90138393,
+ 0x34370733, 0xe427c7e3, 0x24240420, 0xa42484a0, 0xc80bcbc3, 0x50134353, 0x080a0a02, 0x84078783,
+ 0xd819c9d1, 0x4c0c4c40, 0x80038383, 0x8c0f8f83, 0xcc0ecec2, 0x383b0b33, 0x480a4a42, 0xb43787b3
+ };
+
+ private static readonly uint[] SS2 =
+ {
+
+ 0xa1a82989, 0x81840585, 0xd2d416c6, 0xd3d013c3, 0x50541444, 0x111c1d0d, 0xa0ac2c8c, 0x21242505,
+ 0x515c1d4d, 0x43400343, 0x10181808, 0x121c1e0e, 0x51501141, 0xf0fc3ccc, 0xc2c80aca, 0x63602343,
+ 0x20282808, 0x40440444, 0x20202000, 0x919c1d8d, 0xe0e020c0, 0xe2e022c2, 0xc0c808c8, 0x13141707,
+ 0xa1a42585, 0x838c0f8f, 0x03000303, 0x73783b4b, 0xb3b83b8b, 0x13101303, 0xd2d012c2, 0xe2ec2ece,
+ 0x70703040, 0x808c0c8c, 0x333c3f0f, 0xa0a82888, 0x32303202, 0xd1dc1dcd, 0xf2f436c6, 0x70743444,
+ 0xe0ec2ccc, 0x91941585, 0x03080b0b, 0x53541747, 0x505c1c4c, 0x53581b4b, 0xb1bc3d8d, 0x01000101,
+ 0x20242404, 0x101c1c0c, 0x73703343, 0x90981888, 0x10101000, 0xc0cc0ccc, 0xf2f032c2, 0xd1d819c9,
+ 0x202c2c0c, 0xe3e427c7, 0x72703242, 0x83800383, 0x93981b8b, 0xd1d011c1, 0x82840686, 0xc1c809c9,
+ 0x60602040, 0x50501040, 0xa3a02383, 0xe3e82bcb, 0x010c0d0d, 0xb2b43686, 0x929c1e8e, 0x434c0f4f,
+ 0xb3b43787, 0x52581a4a, 0xc2c406c6, 0x70783848, 0xa2a42686, 0x12101202, 0xa3ac2f8f, 0xd1d415c5,
+ 0x61602141, 0xc3c003c3, 0xb0b43484, 0x41400141, 0x52501242, 0x717c3d4d, 0x818c0d8d, 0x00080808,
+ 0x131c1f0f, 0x91981989, 0x00000000, 0x11181909, 0x00040404, 0x53501343, 0xf3f437c7, 0xe1e021c1,
+ 0xf1fc3dcd, 0x72743646, 0x232c2f0f, 0x23242707, 0xb0b03080, 0x83880b8b, 0x020c0e0e, 0xa3a82b8b,
+ 0xa2a02282, 0x626c2e4e, 0x93901383, 0x414c0d4d, 0x61682949, 0x707c3c4c, 0x01080909, 0x02080a0a,
+ 0xb3bc3f8f, 0xe3ec2fcf, 0xf3f033c3, 0xc1c405c5, 0x83840787, 0x10141404, 0xf2fc3ece, 0x60642444,
+ 0xd2dc1ece, 0x222c2e0e, 0x43480b4b, 0x12181a0a, 0x02040606, 0x21202101, 0x63682b4b, 0x62642646,
+ 0x02000202, 0xf1f435c5, 0x92901282, 0x82880a8a, 0x000c0c0c, 0xb3b03383, 0x727c3e4e, 0xd0d010c0,
+ 0x72783a4a, 0x43440747, 0x92941686, 0xe1e425c5, 0x22242606, 0x80800080, 0xa1ac2d8d, 0xd3dc1fcf,
+ 0xa1a02181, 0x30303000, 0x33343707, 0xa2ac2e8e, 0x32343606, 0x11141505, 0x22202202, 0x30383808,
+ 0xf0f434c4, 0xa3a42787, 0x41440545, 0x404c0c4c, 0x81800181, 0xe1e829c9, 0x80840484, 0x93941787,
+ 0x31343505, 0xc3c80bcb, 0xc2cc0ece, 0x303c3c0c, 0x71703141, 0x11101101, 0xc3c407c7, 0x81880989,
+ 0x71743545, 0xf3f83bcb, 0xd2d81aca, 0xf0f838c8, 0x90941484, 0x51581949, 0x82800282, 0xc0c404c4,
+ 0xf3fc3fcf, 0x41480949, 0x31383909, 0x63642747, 0xc0c000c0, 0xc3cc0fcf, 0xd3d417c7, 0xb0b83888,
+ 0x030c0f0f, 0x828c0e8e, 0x42400242, 0x23202303, 0x91901181, 0x606c2c4c, 0xd3d81bcb, 0xa0a42484,
+ 0x30343404, 0xf1f031c1, 0x40480848, 0xc2c002c2, 0x636c2f4f, 0x313c3d0d, 0x212c2d0d, 0x40400040,
+ 0xb2bc3e8e, 0x323c3e0e, 0xb0bc3c8c, 0xc1c001c1, 0xa2a82a8a, 0xb2b83a8a, 0x424c0e4e, 0x51541545,
+ 0x33383b0b, 0xd0dc1ccc, 0x60682848, 0x737c3f4f, 0x909c1c8c, 0xd0d818c8, 0x42480a4a, 0x52541646,
+ 0x73743747, 0xa0a02080, 0xe1ec2dcd, 0x42440646, 0xb1b43585, 0x23282b0b, 0x61642545, 0xf2f83aca,
+ 0xe3e023c3, 0xb1b83989, 0xb1b03181, 0x939c1f8f, 0x525c1e4e, 0xf1f839c9, 0xe2e426c6, 0xb2b03282,
+ 0x31303101, 0xe2e82aca, 0x616c2d4d, 0x535c1f4f, 0xe0e424c4, 0xf0f030c0, 0xc1cc0dcd, 0x80880888,
+ 0x12141606, 0x32383a0a, 0x50581848, 0xd0d414c4, 0x62602242, 0x21282909, 0x03040707, 0x33303303,
+ 0xe0e828c8, 0x13181b0b, 0x01040505, 0x71783949, 0x90901080, 0x62682a4a, 0x22282a0a, 0x92981a8a
+ };
+
+ private static readonly uint[] SS3 =
+ {
+
+ 0x08303838, 0xc8e0e828, 0x0d212c2d, 0x86a2a426, 0xcfc3cc0f, 0xced2dc1e, 0x83b3b033, 0x88b0b838,
+ 0x8fa3ac2f, 0x40606020, 0x45515415, 0xc7c3c407, 0x44404404, 0x4f636c2f, 0x4b63682b, 0x4b53581b,
+ 0xc3c3c003, 0x42626022, 0x03333033, 0x85b1b435, 0x09212829, 0x80a0a020, 0xc2e2e022, 0x87a3a427,
+ 0xc3d3d013, 0x81919011, 0x01111011, 0x06020406, 0x0c101c1c, 0x8cb0bc3c, 0x06323436, 0x4b43480b,
+ 0xcfe3ec2f, 0x88808808, 0x4c606c2c, 0x88a0a828, 0x07131417, 0xc4c0c404, 0x06121416, 0xc4f0f434,
+ 0xc2c2c002, 0x45414405, 0xc1e1e021, 0xc6d2d416, 0x0f333c3f, 0x0d313c3d, 0x8e828c0e, 0x88909818,
+ 0x08202828, 0x4e424c0e, 0xc6f2f436, 0x0e323c3e, 0x85a1a425, 0xc9f1f839, 0x0d010c0d, 0xcfd3dc1f,
+ 0xc8d0d818, 0x0b23282b, 0x46626426, 0x4a72783a, 0x07232427, 0x0f232c2f, 0xc1f1f031, 0x42727032,
+ 0x42424002, 0xc4d0d414, 0x41414001, 0xc0c0c000, 0x43737033, 0x47636427, 0x8ca0ac2c, 0x8b83880b,
+ 0xc7f3f437, 0x8da1ac2d, 0x80808000, 0x0f131c1f, 0xcac2c80a, 0x0c202c2c, 0x8aa2a82a, 0x04303434,
+ 0xc2d2d012, 0x0b03080b, 0xcee2ec2e, 0xc9e1e829, 0x4d515c1d, 0x84909414, 0x08101818, 0xc8f0f838,
+ 0x47535417, 0x8ea2ac2e, 0x08000808, 0xc5c1c405, 0x03131013, 0xcdc1cc0d, 0x86828406, 0x89b1b839,
+ 0xcff3fc3f, 0x4d717c3d, 0xc1c1c001, 0x01313031, 0xc5f1f435, 0x8a82880a, 0x4a62682a, 0x81b1b031,
+ 0xc1d1d011, 0x00202020, 0xc7d3d417, 0x02020002, 0x02222022, 0x04000404, 0x48606828, 0x41717031,
+ 0x07030407, 0xcbd3d81b, 0x8d919c1d, 0x89919819, 0x41616021, 0x8eb2bc3e, 0xc6e2e426, 0x49515819,
+ 0xcdd1dc1d, 0x41515011, 0x80909010, 0xccd0dc1c, 0x8a92981a, 0x83a3a023, 0x8ba3a82b, 0xc0d0d010,
+ 0x81818001, 0x0f030c0f, 0x47434407, 0x0a12181a, 0xc3e3e023, 0xcce0ec2c, 0x8d818c0d, 0x8fb3bc3f,
+ 0x86929416, 0x4b73783b, 0x4c505c1c, 0x82a2a022, 0x81a1a021, 0x43636023, 0x03232023, 0x4d414c0d,
+ 0xc8c0c808, 0x8e929c1e, 0x8c909c1c, 0x0a32383a, 0x0c000c0c, 0x0e222c2e, 0x8ab2b83a, 0x4e626c2e,
+ 0x8f939c1f, 0x4a52581a, 0xc2f2f032, 0x82929012, 0xc3f3f033, 0x49414809, 0x48707838, 0xccc0cc0c,
+ 0x05111415, 0xcbf3f83b, 0x40707030, 0x45717435, 0x4f737c3f, 0x05313435, 0x00101010, 0x03030003,
+ 0x44606424, 0x4d616c2d, 0xc6c2c406, 0x44707434, 0xc5d1d415, 0x84b0b434, 0xcae2e82a, 0x09010809,
+ 0x46727436, 0x09111819, 0xcef2fc3e, 0x40404000, 0x02121012, 0xc0e0e020, 0x8db1bc3d, 0x05010405,
+ 0xcaf2f83a, 0x01010001, 0xc0f0f030, 0x0a22282a, 0x4e525c1e, 0x89a1a829, 0x46525416, 0x43434003,
+ 0x85818405, 0x04101414, 0x89818809, 0x8b93981b, 0x80b0b030, 0xc5e1e425, 0x48404808, 0x49717839,
+ 0x87939417, 0xccf0fc3c, 0x0e121c1e, 0x82828002, 0x01212021, 0x8c808c0c, 0x0b13181b, 0x4f535c1f,
+ 0x47737437, 0x44505414, 0x82b2b032, 0x0d111c1d, 0x05212425, 0x4f434c0f, 0x00000000, 0x46424406,
+ 0xcde1ec2d, 0x48505818, 0x42525012, 0xcbe3e82b, 0x4e727c3e, 0xcad2d81a, 0xc9c1c809, 0xcdf1fc3d,
+ 0x00303030, 0x85919415, 0x45616425, 0x0c303c3c, 0x86b2b436, 0xc4e0e424, 0x8bb3b83b, 0x4c707c3c,
+ 0x0e020c0e, 0x40505010, 0x09313839, 0x06222426, 0x02323032, 0x84808404, 0x49616829, 0x83939013,
+ 0x07333437, 0xc7e3e427, 0x04202424, 0x84a0a424, 0xcbc3c80b, 0x43535013, 0x0a02080a, 0x87838407,
+ 0xc9d1d819, 0x4c404c0c, 0x83838003, 0x8f838c0f, 0xcec2cc0e, 0x0b33383b, 0x4a42480a, 0x87b3b437
+ };
+
+ private static readonly uint[] KC =
+ {
+ 0x9e3779b9, 0x3c6ef373, 0x78dde6e6, 0xf1bbcdcc,
+ 0xe3779b99, 0xc6ef3733, 0x8dde6e67, 0x1bbcdccf,
+ 0x3779b99e, 0x6ef3733c, 0xdde6e678, 0xbbcdccf1,
+ 0x779b99e3, 0xef3733c6, 0xde6e678d, 0xbcdccf1b
+ };
+
+ private int[] wKey;
+ private bool forEncryption;
+
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+ wKey = createWorkingKey(((KeyParameter)parameters).GetKey());
+ }
+
+ public string AlgorithmName
+ {
+ get { return "SEED"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BlockSize;
+ }
+
+ public int ProcessBlock(
+ byte[] inBuf,
+ int inOff,
+ byte[] outBuf,
+ int outOff)
+ {
+ if (wKey == null)
+ throw new InvalidOperationException("SEED engine not initialised");
+ if (inOff + BlockSize > inBuf.Length)
+ throw new DataLengthException("input buffer too short");
+ if (outOff + BlockSize > outBuf.Length)
+ throw new DataLengthException("output buffer too short");
+
+ long l = bytesToLong(inBuf, inOff + 0);
+ long r = bytesToLong(inBuf, inOff + 8);
+
+ if (forEncryption)
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ long nl = r;
+
+ r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r);
+ l = nl;
+ }
+ }
+ else
+ {
+ for (int i = 15; i >= 0; i--)
+ {
+ long nl = r;
+
+ r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r);
+ l = nl;
+ }
+ }
+
+ longToBytes(outBuf, outOff + 0, r);
+ longToBytes(outBuf, outOff + 8, l);
+
+ return BlockSize;
+ }
+
+ public void Reset()
+ {
+ }
+
+ private int[] createWorkingKey(
+ byte[] inKey)
+ {
+ int[] key = new int[32];
+ long lower = bytesToLong(inKey, 0);
+ long upper = bytesToLong(inKey, 8);
+
+ int key0 = extractW0(lower);
+ int key1 = extractW1(lower);
+ int key2 = extractW0(upper);
+ int key3 = extractW1(upper);
+
+ for (int i = 0; i < 16; i++)
+ {
+ key[2 * i] = G(key0 + key2 - (int)KC[i]);
+ key[2 * i + 1] = G(key1 - key3 + (int)KC[i]);
+
+ if (i % 2 == 0)
+ {
+ lower = rotateRight8(lower);
+ key0 = extractW0(lower);
+ key1 = extractW1(lower);
+ }
+ else
+ {
+ upper = rotateLeft8(upper);
+ key2 = extractW0(upper);
+ key3 = extractW1(upper);
+ }
+ }
+
+ return key;
+ }
+
+ private int extractW1(
+ long lVal)
+ {
+ return (int)lVal;
+ }
+
+ private int extractW0(
+ long lVal)
+ {
+ return (int)(lVal >> 32);
+ }
+
+ private long rotateLeft8(
+ long x)
+ {
+ return (x << 8) | ((long)((ulong) x >> 56));
+ }
+
+ private long rotateRight8(
+ long x)
+ {
+ return ((long)((ulong) x >> 8)) | (x << 56);
+ }
+
+ private long bytesToLong(
+ byte[] src,
+ int srcOff)
+ {
+ long word = 0;
+
+ for (int i = 0; i <= 7; i++)
+ {
+ word = (word << 8) + (src[i + srcOff] & 0xff);
+ }
+
+ return word;
+ }
+
+ private void longToBytes(
+ byte[] dest,
+ int destOff,
+ long value)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ dest[i + destOff] = (byte)(value >> ((7 - i) * 8));
+ }
+ }
+
+ private int G(
+ int x)
+ {
+ return (int)(SS0[x & 0xff] ^ SS1[(x >> 8) & 0xff] ^ SS2[(x >> 16) & 0xff] ^ SS3[(x >> 24) & 0xff]);
+ }
+
+ private long F(
+ int ki0,
+ int ki1,
+ long r)
+ {
+ int r0 = (int)(r >> 32);
+ int r1 = (int)r;
+ int rd1 = phaseCalc2(r0, ki0, r1, ki1);
+ int rd0 = rd1 + phaseCalc1(r0, ki0, r1, ki1);
+
+ return ((long)rd0 << 32) | (rd1 & 0xffffffffL);
+ }
+
+ private int phaseCalc1(
+ int r0,
+ int ki0,
+ int r1,
+ int ki1)
+ {
+ return G(G((r0 ^ ki0) ^ (r1 ^ ki1)) + (r0 ^ ki0));
+ }
+
+ private int phaseCalc2(
+ int r0,
+ int ki0,
+ int r1,
+ int ki1)
+ {
+ return G(phaseCalc1(r0, ki0, r1, ki1) + G((r0 ^ ki0) ^ (r1 ^ ki1)));
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/SEEDWrapEngine.cs b/Crypto/src/crypto/engines/SEEDWrapEngine.cs
new file mode 100644
index 000000000..6b71f940b
--- /dev/null
+++ b/Crypto/src/crypto/engines/SEEDWrapEngine.cs
@@ -0,0 +1,16 @@
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /// <remarks>
+ /// An implementation of the SEED key wrapper based on RFC 4010/RFC 3394.
+ /// <p/>
+ /// For further details see: <a href="http://www.ietf.org/rfc/rfc4010.txt">http://www.ietf.org/rfc/rfc4010.txt</a>.
+ /// </remarks>
+ public class SeedWrapEngine
+ : Rfc3394WrapEngine
+ {
+ public SeedWrapEngine()
+ : base(new SeedEngine())
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/Salsa20Engine.cs b/Crypto/src/crypto/engines/Salsa20Engine.cs
new file mode 100644
index 000000000..7d68deab1
--- /dev/null
+++ b/Crypto/src/crypto/engines/Salsa20Engine.cs
@@ -0,0 +1,299 @@
+using System;
+using System.Text;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
+ */
+ public class Salsa20Engine
+ : IStreamCipher
+ {
+ /** Constants */
+ private const int StateSize = 16; // 16, 32 bit ints = 64 bytes
+
+ private readonly static byte[]
+ sigma = Strings.ToAsciiByteArray("expand 32-byte k"),
+ tau = Strings.ToAsciiByteArray("expand 16-byte k");
+
+ /*
+ * variables to hold the state of the engine
+ * during encryption and decryption
+ */
+ private int index = 0;
+ private uint[] engineState = new uint[StateSize]; // state
+ private uint[] x = new uint[StateSize]; // internal buffer
+ private byte[] keyStream = new byte[StateSize * 4], // expanded state, 64 bytes
+ workingKey = null,
+ workingIV = null;
+ private bool initialised = false;
+
+ /*
+ * internal counter
+ */
+ private uint cW0, cW1, cW2;
+
+ /**
+ * initialise a Salsa20 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception ArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ /*
+ * Salsa20 encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant. (Like 90% of stream ciphers)
+ */
+
+ ParametersWithIV ivParams = parameters as ParametersWithIV;
+
+ if (ivParams == null)
+ throw new ArgumentException("Salsa20 Init requires an IV", "parameters");
+
+ byte[] iv = ivParams.GetIV();
+
+ if (iv == null || iv.Length != 8)
+ throw new ArgumentException("Salsa20 requires exactly 8 bytes of IV");
+
+ KeyParameter key = ivParams.Parameters as KeyParameter;
+
+ if (key == null)
+ throw new ArgumentException("Salsa20 Init requires a key", "parameters");
+
+ workingKey = key.GetKey();
+ workingIV = iv;
+
+ SetKey(workingKey, workingIV);
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Salsa20"; }
+ }
+
+ public byte ReturnByte(
+ byte input)
+ {
+ if (LimitExceeded())
+ {
+ throw new MaxBytesExceededException("2^70 byte limit per IV; Change IV");
+ }
+
+ if (index == 0)
+ {
+ GenerateKeyStream(keyStream);
+
+ if (++engineState[8] == 0)
+ {
+ ++engineState[9];
+ }
+ }
+
+ byte output = (byte)(keyStream[index] ^ input);
+ index = (index + 1) & 63;
+
+ return output;
+ }
+
+ public void ProcessBytes(
+ byte[] inBytes,
+ int inOff,
+ int len,
+ byte[] outBytes,
+ int outOff)
+ {
+ if (!initialised)
+ {
+ throw new InvalidOperationException(AlgorithmName + " not initialised");
+ }
+
+ if ((inOff + len) > inBytes.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > outBytes.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (LimitExceeded((uint)len))
+ {
+ throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ if (index == 0)
+ {
+ GenerateKeyStream(keyStream);
+
+ if (++engineState[8] == 0)
+ {
+ ++engineState[9];
+ }
+ }
+ outBytes[i+outOff] = (byte)(keyStream[index]^inBytes[i+inOff]);
+ index = (index + 1) & 63;
+ }
+ }
+
+ public void Reset()
+ {
+ SetKey(workingKey, workingIV);
+ }
+
+ // Private implementation
+
+ private void SetKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ workingKey = keyBytes;
+ workingIV = ivBytes;
+
+ index = 0;
+ ResetCounter();
+ int offset = 0;
+ byte[] constants;
+
+ // Key
+ engineState[1] = Pack.LE_To_UInt32(workingKey, 0);
+ engineState[2] = Pack.LE_To_UInt32(workingKey, 4);
+ engineState[3] = Pack.LE_To_UInt32(workingKey, 8);
+ engineState[4] = Pack.LE_To_UInt32(workingKey, 12);
+
+ if (workingKey.Length == 32)
+ {
+ constants = sigma;
+ offset = 16;
+ }
+ else
+ {
+ constants = tau;
+ }
+
+ engineState[11] = Pack.LE_To_UInt32(workingKey, offset);
+ engineState[12] = Pack.LE_To_UInt32(workingKey, offset + 4);
+ engineState[13] = Pack.LE_To_UInt32(workingKey, offset + 8);
+ engineState[14] = Pack.LE_To_UInt32(workingKey, offset + 12);
+ engineState[0] = Pack.LE_To_UInt32(constants, 0);
+ engineState[5] = Pack.LE_To_UInt32(constants, 4);
+ engineState[10] = Pack.LE_To_UInt32(constants, 8);
+ engineState[15] = Pack.LE_To_UInt32(constants, 12);
+
+ // IV
+ engineState[6] = Pack.LE_To_UInt32(workingIV, 0);
+ engineState[7] = Pack.LE_To_UInt32(workingIV, 4);
+ engineState[8] = engineState[9] = 0;
+
+ initialised = true;
+ }
+
+ private void GenerateKeyStream(byte[] output)
+ {
+ SalsaCore(20, engineState, x);
+ Pack.UInt32_To_LE(x, output, 0);
+ }
+
+ internal static void SalsaCore(int rounds, uint[] state, uint[] x)
+ {
+ // TODO Exception if rounds odd?
+
+ Array.Copy(state, 0, x, 0, state.Length);
+
+ for (int i = rounds; i > 0; i -= 2)
+ {
+ x[ 4] ^= R((x[ 0]+x[12]), 7);
+ x[ 8] ^= R((x[ 4]+x[ 0]), 9);
+ x[12] ^= R((x[ 8]+x[ 4]),13);
+ x[ 0] ^= R((x[12]+x[ 8]),18);
+ x[ 9] ^= R((x[ 5]+x[ 1]), 7);
+ x[13] ^= R((x[ 9]+x[ 5]), 9);
+ x[ 1] ^= R((x[13]+x[ 9]),13);
+ x[ 5] ^= R((x[ 1]+x[13]),18);
+ x[14] ^= R((x[10]+x[ 6]), 7);
+ x[ 2] ^= R((x[14]+x[10]), 9);
+ x[ 6] ^= R((x[ 2]+x[14]),13);
+ x[10] ^= R((x[ 6]+x[ 2]),18);
+ x[ 3] ^= R((x[15]+x[11]), 7);
+ x[ 7] ^= R((x[ 3]+x[15]), 9);
+ x[11] ^= R((x[ 7]+x[ 3]),13);
+ x[15] ^= R((x[11]+x[ 7]),18);
+ x[ 1] ^= R((x[ 0]+x[ 3]), 7);
+ x[ 2] ^= R((x[ 1]+x[ 0]), 9);
+ x[ 3] ^= R((x[ 2]+x[ 1]),13);
+ x[ 0] ^= R((x[ 3]+x[ 2]),18);
+ x[ 6] ^= R((x[ 5]+x[ 4]), 7);
+ x[ 7] ^= R((x[ 6]+x[ 5]), 9);
+ x[ 4] ^= R((x[ 7]+x[ 6]),13);
+ x[ 5] ^= R((x[ 4]+x[ 7]),18);
+ x[11] ^= R((x[10]+x[ 9]), 7);
+ x[ 8] ^= R((x[11]+x[10]), 9);
+ x[ 9] ^= R((x[ 8]+x[11]),13);
+ x[10] ^= R((x[ 9]+x[ 8]),18);
+ x[12] ^= R((x[15]+x[14]), 7);
+ x[13] ^= R((x[12]+x[15]), 9);
+ x[14] ^= R((x[13]+x[12]),13);
+ x[15] ^= R((x[14]+x[13]),18);
+ }
+
+ for (int i = 0; i < StateSize; ++i)
+ {
+ x[i] += state[i];
+ }
+ }
+
+ private static uint R(uint x, int y)
+ {
+ return (x << y) | (x >> (32 - y));
+ }
+
+ private void ResetCounter()
+ {
+ cW0 = 0;
+ cW1 = 0;
+ cW2 = 0;
+ }
+
+ private bool LimitExceeded()
+ {
+ if (++cW0 == 0)
+ {
+ if (++cW1 == 0)
+ {
+ return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ * this relies on the fact len will always be positive.
+ */
+ private bool LimitExceeded(
+ uint len)
+ {
+ uint old = cW0;
+ cW0 += len;
+ if (cW0 < old)
+ {
+ if (++cW1 == 0)
+ {
+ return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/SerpentEngine.cs b/Crypto/src/crypto/engines/SerpentEngine.cs
new file mode 100644
index 000000000..92b25acc6
--- /dev/null
+++ b/Crypto/src/crypto/engines/SerpentEngine.cs
@@ -0,0 +1,779 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * Serpent is a 128-bit 32-round block cipher with variable key lengths,
+ * including 128, 192 and 256 bit keys conjectured to be at least as
+ * secure as three-key triple-DES.
+ * <p>
+ * Serpent was designed by Ross Anderson, Eli Biham and Lars Knudsen as a
+ * candidate algorithm for the NIST AES Quest.>
+ * </p>
+ * <p>
+ * For full details see the <a href="http://www.cl.cam.ac.uk/~rja14/serpent.html">The Serpent home page</a>
+ * </p>
+ */
+ public class SerpentEngine
+ : IBlockCipher
+ {
+ private const int BLOCK_SIZE = 16;
+
+ static readonly int ROUNDS = 32;
+ static readonly int PHI = unchecked((int)0x9E3779B9); // (Sqrt(5) - 1) * 2**31
+
+ private bool encrypting;
+ private int[] wKey;
+
+ private int X0, X1, X2, X3; // registers
+
+ /**
+ * initialise a Serpent cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("invalid parameter passed to Serpent init - " + parameters.GetType().ToString());
+
+ this.encrypting = forEncryption;
+ this.wKey = MakeWorkingKey(((KeyParameter)parameters).GetKey());
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Serpent"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (wKey == null)
+ throw new InvalidOperationException("Serpent not initialised");
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ if (encrypting)
+ {
+ EncryptBlock(input, inOff, output, outOff);
+ }
+ else
+ {
+ DecryptBlock(input, inOff, output, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ }
+
+ /**
+ * Expand a user-supplied key material into a session key.
+ *
+ * @param key The user-key bytes (multiples of 4) to use.
+ * @exception ArgumentException
+ */
+ private int[] MakeWorkingKey(
+ byte[] key)
+ {
+ //
+ // pad key to 256 bits
+ //
+ int[] kPad = new int[16];
+ int off = 0;
+ int length = 0;
+
+ for (off = key.Length - 4; off > 0; off -= 4)
+ {
+ kPad[length++] = BytesToWord(key, off);
+ }
+
+ if (off == 0)
+ {
+ kPad[length++] = BytesToWord(key, 0);
+ if (length < 8)
+ {
+ kPad[length] = 1;
+ }
+ }
+ else
+ {
+ throw new ArgumentException("key must be a multiple of 4 bytes");
+ }
+
+ //
+ // expand the padded key up to 33 x 128 bits of key material
+ //
+ int amount = (ROUNDS + 1) * 4;
+ int[] w = new int[amount];
+
+ //
+ // compute w0 to w7 from w-8 to w-1
+ //
+ for (int i = 8; i < 16; i++)
+ {
+ kPad[i] = RotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11);
+ }
+
+ Array.Copy(kPad, 8, w, 0, 8);
+
+ //
+ // compute w8 to w136
+ //
+ for (int i = 8; i < amount; i++)
+ {
+ w[i] = RotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11);
+ }
+
+ //
+ // create the working keys by processing w with the Sbox and IP
+ //
+ Sb3(w[0], w[1], w[2], w[3]);
+ w[0] = X0; w[1] = X1; w[2] = X2; w[3] = X3;
+ Sb2(w[4], w[5], w[6], w[7]);
+ w[4] = X0; w[5] = X1; w[6] = X2; w[7] = X3;
+ Sb1(w[8], w[9], w[10], w[11]);
+ w[8] = X0; w[9] = X1; w[10] = X2; w[11] = X3;
+ Sb0(w[12], w[13], w[14], w[15]);
+ w[12] = X0; w[13] = X1; w[14] = X2; w[15] = X3;
+ Sb7(w[16], w[17], w[18], w[19]);
+ w[16] = X0; w[17] = X1; w[18] = X2; w[19] = X3;
+ Sb6(w[20], w[21], w[22], w[23]);
+ w[20] = X0; w[21] = X1; w[22] = X2; w[23] = X3;
+ Sb5(w[24], w[25], w[26], w[27]);
+ w[24] = X0; w[25] = X1; w[26] = X2; w[27] = X3;
+ Sb4(w[28], w[29], w[30], w[31]);
+ w[28] = X0; w[29] = X1; w[30] = X2; w[31] = X3;
+ Sb3(w[32], w[33], w[34], w[35]);
+ w[32] = X0; w[33] = X1; w[34] = X2; w[35] = X3;
+ Sb2(w[36], w[37], w[38], w[39]);
+ w[36] = X0; w[37] = X1; w[38] = X2; w[39] = X3;
+ Sb1(w[40], w[41], w[42], w[43]);
+ w[40] = X0; w[41] = X1; w[42] = X2; w[43] = X3;
+ Sb0(w[44], w[45], w[46], w[47]);
+ w[44] = X0; w[45] = X1; w[46] = X2; w[47] = X3;
+ Sb7(w[48], w[49], w[50], w[51]);
+ w[48] = X0; w[49] = X1; w[50] = X2; w[51] = X3;
+ Sb6(w[52], w[53], w[54], w[55]);
+ w[52] = X0; w[53] = X1; w[54] = X2; w[55] = X3;
+ Sb5(w[56], w[57], w[58], w[59]);
+ w[56] = X0; w[57] = X1; w[58] = X2; w[59] = X3;
+ Sb4(w[60], w[61], w[62], w[63]);
+ w[60] = X0; w[61] = X1; w[62] = X2; w[63] = X3;
+ Sb3(w[64], w[65], w[66], w[67]);
+ w[64] = X0; w[65] = X1; w[66] = X2; w[67] = X3;
+ Sb2(w[68], w[69], w[70], w[71]);
+ w[68] = X0; w[69] = X1; w[70] = X2; w[71] = X3;
+ Sb1(w[72], w[73], w[74], w[75]);
+ w[72] = X0; w[73] = X1; w[74] = X2; w[75] = X3;
+ Sb0(w[76], w[77], w[78], w[79]);
+ w[76] = X0; w[77] = X1; w[78] = X2; w[79] = X3;
+ Sb7(w[80], w[81], w[82], w[83]);
+ w[80] = X0; w[81] = X1; w[82] = X2; w[83] = X3;
+ Sb6(w[84], w[85], w[86], w[87]);
+ w[84] = X0; w[85] = X1; w[86] = X2; w[87] = X3;
+ Sb5(w[88], w[89], w[90], w[91]);
+ w[88] = X0; w[89] = X1; w[90] = X2; w[91] = X3;
+ Sb4(w[92], w[93], w[94], w[95]);
+ w[92] = X0; w[93] = X1; w[94] = X2; w[95] = X3;
+ Sb3(w[96], w[97], w[98], w[99]);
+ w[96] = X0; w[97] = X1; w[98] = X2; w[99] = X3;
+ Sb2(w[100], w[101], w[102], w[103]);
+ w[100] = X0; w[101] = X1; w[102] = X2; w[103] = X3;
+ Sb1(w[104], w[105], w[106], w[107]);
+ w[104] = X0; w[105] = X1; w[106] = X2; w[107] = X3;
+ Sb0(w[108], w[109], w[110], w[111]);
+ w[108] = X0; w[109] = X1; w[110] = X2; w[111] = X3;
+ Sb7(w[112], w[113], w[114], w[115]);
+ w[112] = X0; w[113] = X1; w[114] = X2; w[115] = X3;
+ Sb6(w[116], w[117], w[118], w[119]);
+ w[116] = X0; w[117] = X1; w[118] = X2; w[119] = X3;
+ Sb5(w[120], w[121], w[122], w[123]);
+ w[120] = X0; w[121] = X1; w[122] = X2; w[123] = X3;
+ Sb4(w[124], w[125], w[126], w[127]);
+ w[124] = X0; w[125] = X1; w[126] = X2; w[127] = X3;
+ Sb3(w[128], w[129], w[130], w[131]);
+ w[128] = X0; w[129] = X1; w[130] = X2; w[131] = X3;
+
+ return w;
+ }
+
+ private int RotateLeft(
+ int x,
+ int bits)
+ {
+ return ((x << bits) | (int) ((uint)x >> (32 - bits)));
+ }
+
+ private int RotateRight(
+ int x,
+ int bits)
+ {
+ return ( (int)((uint)x >> bits) | (x << (32 - bits)));
+ }
+
+ private int BytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ return (((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) |
+ ((src[srcOff + 2] & 0xff) << 8) | ((src[srcOff + 3] & 0xff)));
+ }
+
+ private void WordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ dst[dstOff + 3] = (byte)(word);
+ dst[dstOff + 2] = (byte)((uint)word >> 8);
+ dst[dstOff + 1] = (byte)((uint)word >> 16);
+ dst[dstOff] = (byte)((uint)word >> 24);
+ }
+
+ /**
+ * Encrypt one block of plaintext.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ private void EncryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ X3 = BytesToWord(input, inOff);
+ X2 = BytesToWord(input, inOff + 4);
+ X1 = BytesToWord(input, inOff + 8);
+ X0 = BytesToWord(input, inOff + 12);
+
+ Sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT();
+ Sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT();
+ Sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT();
+ Sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT();
+ Sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT();
+ Sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT();
+ Sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT();
+ Sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT();
+ Sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT();
+ Sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT();
+ Sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT();
+ Sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT();
+ Sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT();
+ Sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT();
+ Sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT();
+ Sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT();
+ Sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT();
+ Sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT();
+ Sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT();
+ Sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT();
+ Sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT();
+ Sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT();
+ Sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT();
+ Sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT();
+ Sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT();
+ Sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT();
+ Sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT();
+ Sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT();
+ Sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT();
+ Sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT();
+ Sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT();
+ Sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3);
+
+ WordToBytes(wKey[131] ^ X3, outBytes, outOff);
+ WordToBytes(wKey[130] ^ X2, outBytes, outOff + 4);
+ WordToBytes(wKey[129] ^ X1, outBytes, outOff + 8);
+ WordToBytes(wKey[128] ^ X0, outBytes, outOff + 12);
+ }
+
+ /**
+ * Decrypt one block of ciphertext.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ private void DecryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ X3 = wKey[131] ^ BytesToWord(input, inOff);
+ X2 = wKey[130] ^ BytesToWord(input, inOff + 4);
+ X1 = wKey[129] ^ BytesToWord(input, inOff + 8);
+ X0 = wKey[128] ^ BytesToWord(input, inOff + 12);
+
+ Ib7(X0, X1, X2, X3);
+ X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127];
+ InverseLT(); Ib6(X0, X1, X2, X3);
+ X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123];
+ InverseLT(); Ib5(X0, X1, X2, X3);
+ X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119];
+ InverseLT(); Ib4(X0, X1, X2, X3);
+ X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115];
+ InverseLT(); Ib3(X0, X1, X2, X3);
+ X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111];
+ InverseLT(); Ib2(X0, X1, X2, X3);
+ X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107];
+ InverseLT(); Ib1(X0, X1, X2, X3);
+ X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103];
+ InverseLT(); Ib0(X0, X1, X2, X3);
+ X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99];
+ InverseLT(); Ib7(X0, X1, X2, X3);
+ X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95];
+ InverseLT(); Ib6(X0, X1, X2, X3);
+ X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91];
+ InverseLT(); Ib5(X0, X1, X2, X3);
+ X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87];
+ InverseLT(); Ib4(X0, X1, X2, X3);
+ X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83];
+ InverseLT(); Ib3(X0, X1, X2, X3);
+ X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79];
+ InverseLT(); Ib2(X0, X1, X2, X3);
+ X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75];
+ InverseLT(); Ib1(X0, X1, X2, X3);
+ X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71];
+ InverseLT(); Ib0(X0, X1, X2, X3);
+ X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67];
+ InverseLT(); Ib7(X0, X1, X2, X3);
+ X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63];
+ InverseLT(); Ib6(X0, X1, X2, X3);
+ X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59];
+ InverseLT(); Ib5(X0, X1, X2, X3);
+ X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55];
+ InverseLT(); Ib4(X0, X1, X2, X3);
+ X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51];
+ InverseLT(); Ib3(X0, X1, X2, X3);
+ X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47];
+ InverseLT(); Ib2(X0, X1, X2, X3);
+ X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43];
+ InverseLT(); Ib1(X0, X1, X2, X3);
+ X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39];
+ InverseLT(); Ib0(X0, X1, X2, X3);
+ X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35];
+ InverseLT(); Ib7(X0, X1, X2, X3);
+ X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31];
+ InverseLT(); Ib6(X0, X1, X2, X3);
+ X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27];
+ InverseLT(); Ib5(X0, X1, X2, X3);
+ X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23];
+ InverseLT(); Ib4(X0, X1, X2, X3);
+ X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19];
+ InverseLT(); Ib3(X0, X1, X2, X3);
+ X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15];
+ InverseLT(); Ib2(X0, X1, X2, X3);
+ X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11];
+ InverseLT(); Ib1(X0, X1, X2, X3);
+ X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7];
+ InverseLT(); Ib0(X0, X1, X2, X3);
+
+ WordToBytes(X3 ^ wKey[3], outBytes, outOff);
+ WordToBytes(X2 ^ wKey[2], outBytes, outOff + 4);
+ WordToBytes(X1 ^ wKey[1], outBytes, outOff + 8);
+ WordToBytes(X0 ^ wKey[0], outBytes, outOff + 12);
+ }
+
+ /*
+ * The sboxes below are based on the work of Brian Gladman and
+ * Sam Simpson, whose original notice appears below.
+ * <p>
+ * For further details see:
+ * http://fp.gladman.plus.com/cryptography_technology/serpent/
+ * </p>
+ */
+
+ /* Partially optimised Serpent S Box bool functions derived */
+ /* using a recursive descent analyser but without a full search */
+ /* of all subtrees. This set of S boxes is the result of work */
+ /* by Sam Simpson and Brian Gladman using the spare time on a */
+ /* cluster of high capacity servers to search for S boxes with */
+ /* this customised search engine. There are now an average of */
+ /* 15.375 terms per S box. */
+ /* */
+ /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */
+ /* and Sam Simpson (s.simpson@mia.co.uk) */
+ /* 17th December 1998 */
+ /* */
+ /* We hereby give permission for information in this file to be */
+ /* used freely subject only to acknowledgement of its origin. */
+
+ /**
+ * S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms.
+ */
+ private void Sb0(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t3 = c ^ t1;
+ int t4 = b ^ t3;
+ X3 = (a & d) ^ t4;
+ int t7 = a ^ (b & t1);
+ X2 = t4 ^ (c | t7);
+ int t12 = X3 & (t3 ^ t7);
+ X1 = (~t3) ^ t12;
+ X0 = t12 ^ (~t7);
+ }
+
+ /**
+ * InvSO - {13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2 } - 15 terms.
+ */
+ private void Ib0(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t4 = d ^ (t1 | t2);
+ int t5 = c ^ t4;
+ X2 = t2 ^ t5;
+ int t8 = t1 ^ (d & t2);
+ X1 = t4 ^ (X2 & t8);
+ X3 = (a & t4) ^ (t5 | X1);
+ X0 = X3 ^ (t5 ^ t8);
+ }
+
+ /**
+ * S1 - {15,12, 2, 7, 9, 0, 5,10, 1,11,14, 8, 6,13, 3, 4 } - 14 terms.
+ */
+ private void Sb1(int a, int b, int c, int d)
+ {
+ int t2 = b ^ (~a);
+ int t5 = c ^ (a | t2);
+ X2 = d ^ t5;
+ int t7 = b ^ (d | t2);
+ int t8 = t2 ^ X2;
+ X3 = t8 ^ (t5 & t7);
+ int t11 = t5 ^ t7;
+ X1 = X3 ^ t11;
+ X0 = t5 ^ (t8 & t11);
+ }
+
+ /**
+ * InvS1 - { 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0 } - 14 steps.
+ */
+ private void Ib1(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t3 = a ^ (b & t1);
+ int t4 = t1 ^ t3;
+ X3 = c ^ t4;
+ int t7 = b ^ (t1 & t3);
+ int t8 = X3 | t7;
+ X1 = t3 ^ t8;
+ int t10 = ~X1;
+ int t11 = X3 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = t4 ^ (t10 | t11);
+ }
+
+ /**
+ * S2 - { 8, 6, 7, 9, 3,12,10,15,13, 1,14, 4, 0,11, 5, 2 } - 16 terms.
+ */
+ private void Sb2(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = b ^ d;
+ int t3 = c & t1;
+ X0 = t2 ^ t3;
+ int t5 = c ^ t1;
+ int t6 = c ^ X0;
+ int t7 = b & t6;
+ X3 = t5 ^ t7;
+ X2 = a ^ ((d | t7) & (X0 | t5));
+ X1 = (t2 ^ X3) ^ (X2 ^ (d | t1));
+ }
+
+ /**
+ * InvS2 - {12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7 } - 16 steps.
+ */
+ private void Ib2(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t2 = ~t1;
+ int t3 = a ^ c;
+ int t4 = c ^ t1;
+ int t5 = b & t4;
+ X0 = t3 ^ t5;
+ int t7 = a | t2;
+ int t8 = d ^ t7;
+ int t9 = t3 | t8;
+ X3 = t1 ^ t9;
+ int t11 = ~t4;
+ int t12 = X0 | X3;
+ X1 = t11 ^ t12;
+ X2 = (d & t11) ^ (t3 ^ t12);
+ }
+
+ /**
+ * S3 - { 0,15,11, 8,12, 9, 6, 3,13, 1, 2, 4,10, 7, 5,14 } - 16 terms.
+ */
+ private void Sb3(int a, int b, int c, int d)
+ {
+ int t1 = a ^ b;
+ int t2 = a & c;
+ int t3 = a | d;
+ int t4 = c ^ d;
+ int t5 = t1 & t3;
+ int t6 = t2 | t5;
+ X2 = t4 ^ t6;
+ int t8 = b ^ t3;
+ int t9 = t6 ^ t8;
+ int t10 = t4 & t9;
+ X0 = t1 ^ t10;
+ int t12 = X2 & X0;
+ X1 = t9 ^ t12;
+ X3 = (b | d) ^ (t4 ^ t12);
+ }
+
+ /**
+ * InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms
+ */
+ private void Ib3(int a, int b, int c, int d)
+ {
+ int t1 = a | b;
+ int t2 = b ^ c;
+ int t3 = b & t2;
+ int t4 = a ^ t3;
+ int t5 = c ^ t4;
+ int t6 = d | t4;
+ X0 = t2 ^ t6;
+ int t8 = t2 | t6;
+ int t9 = d ^ t8;
+ X2 = t5 ^ t9;
+ int t11 = t1 ^ t9;
+ int t12 = X0 & t11;
+ X3 = t4 ^ t12;
+ X1 = X3 ^ (X0 ^ t11);
+ }
+
+ /**
+ * S4 - { 1,15, 8, 3,12, 0,11, 6, 2, 5, 4,10, 9,14, 7,13 } - 15 terms.
+ */
+ private void Sb4(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t2 = d & t1;
+ int t3 = c ^ t2;
+ int t4 = b | t3;
+ X3 = t1 ^ t4;
+ int t6 = ~b;
+ int t7 = t1 | t6;
+ X0 = t3 ^ t7;
+ int t9 = a & X0;
+ int t10 = t1 ^ t6;
+ int t11 = t4 & t10;
+ X2 = t9 ^ t11;
+ X1 = (a ^ t3) ^ (t10 & X2);
+ }
+
+ /**
+ * InvS4 - { 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1 } - 15 terms.
+ */
+ private void Ib4(int a, int b, int c, int d)
+ {
+ int t1 = c | d;
+ int t2 = a & t1;
+ int t3 = b ^ t2;
+ int t4 = a & t3;
+ int t5 = c ^ t4;
+ X1 = d ^ t5;
+ int t7 = ~a;
+ int t8 = t5 & X1;
+ X3 = t3 ^ t8;
+ int t10 = X1 | t7;
+ int t11 = d ^ t10;
+ X0 = X3 ^ t11;
+ X2 = (t3 & t11) ^ (X1 ^ t7);
+ }
+
+ /**
+ * S5 - {15, 5, 2,11, 4,10, 9,12, 0, 3,14, 8,13, 6, 7, 1 } - 16 terms.
+ */
+ private void Sb5(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = a ^ d;
+ int t4 = c ^ t1;
+ int t5 = t2 | t3;
+ X0 = t4 ^ t5;
+ int t7 = d & X0;
+ int t8 = t2 ^ X0;
+ X1 = t7 ^ t8;
+ int t10 = t1 | X0;
+ int t11 = t2 | t7;
+ int t12 = t3 ^ t10;
+ X2 = t11 ^ t12;
+ X3 = (b ^ t7) ^ (X1 & t12);
+ }
+
+ /**
+ * InvS5 - { 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0 } - 16 terms.
+ */
+ private void Ib5(int a, int b, int c, int d)
+ {
+ int t1 = ~c;
+ int t2 = b & t1;
+ int t3 = d ^ t2;
+ int t4 = a & t3;
+ int t5 = b ^ t1;
+ X3 = t4 ^ t5;
+ int t7 = b | X3;
+ int t8 = a & t7;
+ X1 = t3 ^ t8;
+ int t10 = a | d;
+ int t11 = t1 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = (b & t10) ^ (t4 | (a ^ c));
+ }
+
+ /**
+ * S6 - { 7, 2,12, 5, 8, 4, 6,11,14, 9, 1,15,13, 3,10, 0 } - 15 terms.
+ */
+ private void Sb6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ d;
+ int t3 = b ^ t2;
+ int t4 = t1 | t2;
+ int t5 = c ^ t4;
+ X1 = b ^ t5;
+ int t7 = t2 | X1;
+ int t8 = d ^ t7;
+ int t9 = t5 & t8;
+ X2 = t3 ^ t9;
+ int t11 = t5 ^ t8;
+ X0 = X2 ^ t11;
+ X3 = (~t5) ^ (t3 & t11);
+ }
+
+ /**
+ * InvS6 - {15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11 } - 15 terms.
+ */
+ private void Ib6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = c ^ t2;
+ int t4 = c | t1;
+ int t5 = d ^ t4;
+ X1 = t3 ^ t5;
+ int t7 = t3 & t5;
+ int t8 = t2 ^ t7;
+ int t9 = b | t8;
+ X3 = t5 ^ t9;
+ int t11 = b | X3;
+ X0 = t8 ^ t11;
+ X2 = (d & t1) ^ (t3 ^ t11);
+ }
+
+ /**
+ * S7 - { 1,13,15, 0,14, 8, 2,11, 7, 4,12,10, 9, 3, 5, 6 } - 16 terms.
+ */
+ private void Sb7(int a, int b, int c, int d)
+ {
+ int t1 = b ^ c;
+ int t2 = c & t1;
+ int t3 = d ^ t2;
+ int t4 = a ^ t3;
+ int t5 = d | t1;
+ int t6 = t4 & t5;
+ X1 = b ^ t6;
+ int t8 = t3 | X1;
+ int t9 = a & t4;
+ X3 = t1 ^ t9;
+ int t11 = t4 ^ t8;
+ int t12 = X3 & t11;
+ X2 = t3 ^ t12;
+ X0 = (~t11) ^ (X3 & X2);
+ }
+
+ /**
+ * InvS7 - { 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 } - 17 terms.
+ */
+ private void Ib7(int a, int b, int c, int d)
+ {
+ int t3 = c | (a & b);
+ int t4 = d & (a | b);
+ X3 = t3 ^ t4;
+ int t6 = ~d;
+ int t7 = b ^ t4;
+ int t9 = t7 | (X3 ^ t6);
+ X1 = a ^ t9;
+ X0 = (c ^ t7) ^ (d | X1);
+ X2 = (t3 ^ X1) ^ (X0 ^ (a & X3));
+ }
+
+ /**
+ * Apply the linear transformation to the register set.
+ */
+ private void LT()
+ {
+ int x0 = RotateLeft(X0, 13);
+ int x2 = RotateLeft(X2, 3);
+ int x1 = X1 ^ x0 ^ x2 ;
+ int x3 = X3 ^ x2 ^ x0 << 3;
+
+ X1 = RotateLeft(x1, 1);
+ X3 = RotateLeft(x3, 7);
+ X0 = RotateLeft(x0 ^ X1 ^ X3, 5);
+ X2 = RotateLeft(x2 ^ X3 ^ (X1 << 7), 22);
+ }
+
+ /**
+ * Apply the inverse of the linear transformation to the register set.
+ */
+ private void InverseLT()
+ {
+ int x2 = RotateRight(X2, 22) ^ X3 ^ (X1 << 7);
+ int x0 = RotateRight(X0, 5) ^ X1 ^ X3;
+ int x3 = RotateRight(X3, 7);
+ int x1 = RotateRight(X1, 1);
+ X3 = x3 ^ x2 ^ x0 << 3;
+ X1 = x1 ^ x0 ^ x2;
+ X2 = RotateRight(x2, 3);
+ X0 = RotateRight(x0, 13);
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/SkipjackEngine.cs b/Crypto/src/crypto/engines/SkipjackEngine.cs
new file mode 100644
index 000000000..3d2a781e6
--- /dev/null
+++ b/Crypto/src/crypto/engines/SkipjackEngine.cs
@@ -0,0 +1,255 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * a class that provides a basic SKIPJACK engine.
+ */
+ public class SkipjackEngine
+ : IBlockCipher
+ {
+ const int BLOCK_SIZE = 8;
+
+ static readonly short [] ftable =
+ {
+ 0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9,
+ 0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28,
+ 0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68, 0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53,
+ 0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19, 0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2,
+ 0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b, 0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8,
+ 0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0, 0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90,
+ 0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69, 0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76,
+ 0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20, 0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d,
+ 0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43, 0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18,
+ 0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa, 0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4,
+ 0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87, 0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40,
+ 0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b, 0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5,
+ 0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0, 0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2,
+ 0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1, 0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8,
+ 0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5, 0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac,
+ 0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3, 0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46
+ };
+
+ private int[] key0, key1, key2, key3;
+ private bool encrypting;
+
+ /**
+ * initialise a SKIPJACK cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("invalid parameter passed to SKIPJACK init - " + parameters.GetType().ToString());
+
+ byte[] keyBytes = ((KeyParameter)parameters).GetKey();
+
+ this.encrypting = forEncryption;
+ this.key0 = new int[32];
+ this.key1 = new int[32];
+ this.key2 = new int[32];
+ this.key3 = new int[32];
+
+ //
+ // expand the key to 128 bytes in 4 parts (saving us a modulo, multiply
+ // and an addition).
+ //
+ for (int i = 0; i < 32; i ++)
+ {
+ key0[i] = keyBytes[(i * 4) % 10] & 0xff;
+ key1[i] = keyBytes[(i * 4 + 1) % 10] & 0xff;
+ key2[i] = keyBytes[(i * 4 + 2) % 10] & 0xff;
+ key3[i] = keyBytes[(i * 4 + 3) % 10] & 0xff;
+ }
+ }
+
+ public string AlgorithmName
+ {
+ get { return "SKIPJACK"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (key1 == null)
+ throw new InvalidOperationException("SKIPJACK engine not initialised");
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ if (encrypting)
+ {
+ EncryptBlock(input, inOff, output, outOff);
+ }
+ else
+ {
+ DecryptBlock(input, inOff, output, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ }
+
+ /**
+ * The G permutation
+ */
+ private int G(
+ int k,
+ int w)
+ {
+ int g1, g2, g3, g4, g5, g6;
+
+ g1 = (w >> 8) & 0xff;
+ g2 = w & 0xff;
+
+ g3 = ftable[g2 ^ key0[k]] ^ g1;
+ g4 = ftable[g3 ^ key1[k]] ^ g2;
+ g5 = ftable[g4 ^ key2[k]] ^ g3;
+ g6 = ftable[g5 ^ key3[k]] ^ g4;
+
+ return ((g5 << 8) + g6);
+ }
+
+ public int EncryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ int w1 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff);
+ int w2 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff);
+ int w3 = (input[inOff + 4] << 8) + (input[inOff + 5] & 0xff);
+ int w4 = (input[inOff + 6] << 8) + (input[inOff + 7] & 0xff);
+
+ int k = 0;
+
+ for (int t = 0; t < 2; t++)
+ {
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w2;
+ w2 = G(k, w1);
+ w1 = w2 ^ tmp ^ (k + 1);
+ k++;
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w1 ^ w2 ^ (k + 1);
+ w2 = G(k, w1);
+ w1 = tmp;
+ k++;
+ }
+ }
+
+ outBytes[outOff + 0] = (byte)((w1 >> 8));
+ outBytes[outOff + 1] = (byte)(w1);
+ outBytes[outOff + 2] = (byte)((w2 >> 8));
+ outBytes[outOff + 3] = (byte)(w2);
+ outBytes[outOff + 4] = (byte)((w3 >> 8));
+ outBytes[outOff + 5] = (byte)(w3);
+ outBytes[outOff + 6] = (byte)((w4 >> 8));
+ outBytes[outOff + 7] = (byte)(w4);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * the inverse of the G permutation.
+ */
+ private int H(
+ int k,
+ int w)
+ {
+ int h1, h2, h3, h4, h5, h6;
+
+ h1 = w & 0xff;
+ h2 = (w >> 8) & 0xff;
+
+ h3 = ftable[h2 ^ key3[k]] ^ h1;
+ h4 = ftable[h3 ^ key2[k]] ^ h2;
+ h5 = ftable[h4 ^ key1[k]] ^ h3;
+ h6 = ftable[h5 ^ key0[k]] ^ h4;
+
+ return ((h6 << 8) + h5);
+ }
+
+ public int DecryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ int w2 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff);
+ int w1 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff);
+ int w4 = (input[inOff + 4] << 8) + (input[inOff + 5] & 0xff);
+ int w3 = (input[inOff + 6] << 8) + (input[inOff + 7] & 0xff);
+
+ int k = 31;
+
+ for (int t = 0; t < 2; t++)
+ {
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w2;
+ w2 = H(k, w1);
+ w1 = w2 ^ tmp ^ (k + 1);
+ k--;
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w1 ^ w2 ^ (k + 1);
+ w2 = H(k, w1);
+ w1 = tmp;
+ k--;
+ }
+ }
+
+ outBytes[outOff + 0] = (byte)((w2 >> 8));
+ outBytes[outOff + 1] = (byte)(w2);
+ outBytes[outOff + 2] = (byte)((w1 >> 8));
+ outBytes[outOff + 3] = (byte)(w1);
+ outBytes[outOff + 4] = (byte)((w4 >> 8));
+ outBytes[outOff + 5] = (byte)(w4);
+ outBytes[outOff + 6] = (byte)((w3 >> 8));
+ outBytes[outOff + 7] = (byte)(w3);
+
+ return BLOCK_SIZE;
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/TEAEngine.cs b/Crypto/src/crypto/engines/TEAEngine.cs
new file mode 100644
index 000000000..582dd0f73
--- /dev/null
+++ b/Crypto/src/crypto/engines/TEAEngine.cs
@@ -0,0 +1,168 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * An TEA engine.
+ */
+ public class TeaEngine
+ : IBlockCipher
+ {
+ private const int
+ rounds = 32,
+ block_size = 8;
+// key_size = 16,
+
+ private const uint
+ delta = 0x9E3779B9,
+ d_sum = 0xC6EF3720; // sum on decrypt
+
+ /*
+ * the expanded key array of 4 subkeys
+ */
+ private uint _a, _b, _c, _d;
+ private bool _initialised;
+ private bool _forEncryption;
+
+ /**
+ * Create an instance of the TEA encryption algorithm
+ * and set some defaults
+ */
+ public TeaEngine()
+ {
+ _initialised = false;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "TEA"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return block_size;
+ }
+
+ /**
+ * initialise
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception ArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ {
+ throw new ArgumentException("invalid parameter passed to TEA init - "
+ + parameters.GetType().FullName);
+ }
+
+ _forEncryption = forEncryption;
+ _initialised = true;
+
+ KeyParameter p = (KeyParameter) parameters;
+
+ setKey(p.GetKey());
+ }
+
+ public int ProcessBlock(
+ byte[] inBytes,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ if (!_initialised)
+ throw new InvalidOperationException(AlgorithmName + " not initialised");
+
+ if ((inOff + block_size) > inBytes.Length)
+ throw new DataLengthException("input buffer too short");
+
+ if ((outOff + block_size) > outBytes.Length)
+ throw new DataLengthException("output buffer too short");
+
+ return _forEncryption
+ ? encryptBlock(inBytes, inOff, outBytes, outOff)
+ : decryptBlock(inBytes, inOff, outBytes, outOff);
+ }
+
+ public void Reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ *
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ _a = Pack.BE_To_UInt32(key, 0);
+ _b = Pack.BE_To_UInt32(key, 4);
+ _c = Pack.BE_To_UInt32(key, 8);
+ _d = Pack.BE_To_UInt32(key, 12);
+ }
+
+ private int encryptBlock(
+ byte[] inBytes,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ // Pack bytes into integers
+ uint v0 = Pack.BE_To_UInt32(inBytes, inOff);
+ uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4);
+
+ uint sum = 0;
+
+ for (int i = 0; i != rounds; i++)
+ {
+ sum += delta;
+ v0 += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >> 5) + _b);
+ v1 += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >> 5) + _d);
+ }
+
+ Pack.UInt32_To_BE(v0, outBytes, outOff);
+ Pack.UInt32_To_BE(v1, outBytes, outOff + 4);
+
+ return block_size;
+ }
+
+ private int decryptBlock(
+ byte[] inBytes,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ // Pack bytes into integers
+ uint v0 = Pack.BE_To_UInt32(inBytes, inOff);
+ uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4);
+
+ uint sum = d_sum;
+
+ for (int i = 0; i != rounds; i++)
+ {
+ v1 -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >> 5) + _d);
+ v0 -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >> 5) + _b);
+ sum -= delta;
+ }
+
+ Pack.UInt32_To_BE(v0, outBytes, outOff);
+ Pack.UInt32_To_BE(v1, outBytes, outOff + 4);
+
+ return block_size;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/TwofishEngine.cs b/Crypto/src/crypto/engines/TwofishEngine.cs
new file mode 100644
index 000000000..b983d9d31
--- /dev/null
+++ b/Crypto/src/crypto/engines/TwofishEngine.cs
@@ -0,0 +1,675 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * A class that provides Twofish encryption operations.
+ *
+ * This Java implementation is based on the Java reference
+ * implementation provided by Bruce Schneier and developed
+ * by Raif S. Naffah.
+ */
+ public sealed class TwofishEngine
+ : IBlockCipher
+ {
+ private static readonly byte[,] P = {
+ { // p0
+ (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8,
+ (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76,
+ (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78,
+ (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38,
+ (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98,
+ (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C,
+ (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26,
+ (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48,
+ (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30,
+ (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23,
+ (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59,
+ (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82,
+ (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E,
+ (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C,
+ (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE,
+ (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61,
+ (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5,
+ (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B,
+ (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B,
+ (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1,
+ (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45,
+ (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66,
+ (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56,
+ (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7,
+ (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5,
+ (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA,
+ (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF,
+ (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71,
+ (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD,
+ (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8,
+ (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D,
+ (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7,
+ (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED,
+ (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2,
+ (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11,
+ (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90,
+ (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF,
+ (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB,
+ (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B,
+ (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF,
+ (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE,
+ (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B,
+ (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46,
+ (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64,
+ (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F,
+ (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A,
+ (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A,
+ (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A,
+ (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29,
+ (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02,
+ (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17,
+ (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D,
+ (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74,
+ (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72,
+ (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12,
+ (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34,
+ (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68,
+ (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8,
+ (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40,
+ (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4,
+ (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0,
+ (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00,
+ (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42,
+ (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0 },
+ { // p1
+ (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4,
+ (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8,
+ (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B,
+ (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B,
+ (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD,
+ (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1,
+ (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B,
+ (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F,
+ (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B,
+ (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D,
+ (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E,
+ (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5,
+ (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14,
+ (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3,
+ (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54,
+ (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51,
+ (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A,
+ (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96,
+ (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10,
+ (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C,
+ (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7,
+ (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70,
+ (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB,
+ (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8,
+ (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF,
+ (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC,
+ (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF,
+ (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2,
+ (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82,
+ (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9,
+ (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97,
+ (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17,
+ (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D,
+ (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3,
+ (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C,
+ (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E,
+ (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F,
+ (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49,
+ (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21,
+ (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9,
+ (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD,
+ (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01,
+ (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F,
+ (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48,
+ (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E,
+ (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19,
+ (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57,
+ (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64,
+ (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE,
+ (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5,
+ (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44,
+ (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69,
+ (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15,
+ (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E,
+ (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34,
+ (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC,
+ (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B,
+ (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB,
+ (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52,
+ (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9,
+ (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4,
+ (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2,
+ (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56,
+ (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 }
+ };
+
+ /**
+ * Define the fixed p0/p1 permutations used in keyed S-box lookup.
+ * By changing the following constant definitions, the S-boxes will
+ * automatically Get changed in the Twofish engine.
+ */
+ private const int P_00 = 1;
+ private const int P_01 = 0;
+ private const int P_02 = 0;
+ private const int P_03 = P_01 ^ 1;
+ private const int P_04 = 1;
+
+ private const int P_10 = 0;
+ private const int P_11 = 0;
+ private const int P_12 = 1;
+ private const int P_13 = P_11 ^ 1;
+ private const int P_14 = 0;
+
+ private const int P_20 = 1;
+ private const int P_21 = 1;
+ private const int P_22 = 0;
+ private const int P_23 = P_21 ^ 1;
+ private const int P_24 = 0;
+
+ private const int P_30 = 0;
+ private const int P_31 = 1;
+ private const int P_32 = 1;
+ private const int P_33 = P_31 ^ 1;
+ private const int P_34 = 1;
+
+ /* Primitive polynomial for GF(256) */
+ private const int GF256_FDBK = 0x169;
+ private const int GF256_FDBK_2 = GF256_FDBK / 2;
+ private const int GF256_FDBK_4 = GF256_FDBK / 4;
+
+ private const int RS_GF_FDBK = 0x14D; // field generator
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ private const int ROUNDS = 16;
+ private const int MAX_ROUNDS = 16; // bytes = 128 bits
+ private const int BLOCK_SIZE = 16; // bytes = 128 bits
+ private const int MAX_KEY_BITS = 256;
+
+ private const int INPUT_WHITEN=0;
+ private const int OUTPUT_WHITEN=INPUT_WHITEN+BLOCK_SIZE/4; // 4
+ private const int ROUND_SUBKEYS=OUTPUT_WHITEN+BLOCK_SIZE/4;// 8
+
+ private const int TOTAL_SUBKEYS=ROUND_SUBKEYS+2*MAX_ROUNDS;// 40
+
+ private const int SK_STEP = 0x02020202;
+ private const int SK_BUMP = 0x01010101;
+ private const int SK_ROTL = 9;
+
+ private bool encrypting;
+
+ private int[] gMDS0 = new int[MAX_KEY_BITS];
+ private int[] gMDS1 = new int[MAX_KEY_BITS];
+ private int[] gMDS2 = new int[MAX_KEY_BITS];
+ private int[] gMDS3 = new int[MAX_KEY_BITS];
+
+ /**
+ * gSubKeys[] and gSBox[] are eventually used in the
+ * encryption and decryption methods.
+ */
+ private int[] gSubKeys;
+ private int[] gSBox;
+
+ private int k64Cnt;
+
+ private byte[] workingKey;
+
+ public TwofishEngine()
+ {
+ // calculate the MDS matrix
+ int[] m1 = new int[2];
+ int[] mX = new int[2];
+ int[] mY = new int[2];
+ int j;
+
+ for (int i=0; i< MAX_KEY_BITS ; i++)
+ {
+ j = P[0,i] & 0xff;
+ m1[0] = j;
+ mX[0] = Mx_X(j) & 0xff;
+ mY[0] = Mx_Y(j) & 0xff;
+
+ j = P[1,i] & 0xff;
+ m1[1] = j;
+ mX[1] = Mx_X(j) & 0xff;
+ mY[1] = Mx_Y(j) & 0xff;
+
+ gMDS0[i] = m1[P_00] | mX[P_00] << 8 |
+ mY[P_00] << 16 | mY[P_00] << 24;
+
+ gMDS1[i] = mY[P_10] | mY[P_10] << 8 |
+ mX[P_10] << 16 | m1[P_10] << 24;
+
+ gMDS2[i] = mX[P_20] | mY[P_20] << 8 |
+ m1[P_20] << 16 | mY[P_20] << 24;
+
+ gMDS3[i] = mX[P_30] | m1[P_30] << 8 |
+ mY[P_30] << 16 | mX[P_30] << 24;
+ }
+ }
+
+ /**
+ * initialise a Twofish cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param parameters the parameters required to set up the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("invalid parameter passed to Twofish init - " + parameters.GetType().ToString());
+
+ this.encrypting = forEncryption;
+ this.workingKey = ((KeyParameter)parameters).GetKey();
+ this.k64Cnt = (this.workingKey.Length / 8); // pre-padded ?
+ SetKey(this.workingKey);
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Twofish"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if (workingKey == null)
+ throw new InvalidOperationException("Twofish not initialised");
+ if ((inOff + BLOCK_SIZE) > input.Length)
+ throw new DataLengthException("input buffer too short");
+ if ((outOff + BLOCK_SIZE) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ if (encrypting)
+ {
+ EncryptBlock(input, inOff, output, outOff);
+ }
+ else
+ {
+ DecryptBlock(input, inOff, output, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ if (this.workingKey != null)
+ {
+ SetKey(this.workingKey);
+ }
+ }
+
+ public int GetBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ private void SetKey(byte[] key)
+ {
+ int[] k32e = new int[MAX_KEY_BITS/64]; // 4
+ int[] k32o = new int[MAX_KEY_BITS/64]; // 4
+
+ int[] sBoxKeys = new int[MAX_KEY_BITS/64]; // 4
+ gSubKeys = new int[TOTAL_SUBKEYS];
+
+ if (k64Cnt < 1)
+ {
+ throw new ArgumentException("Key size less than 64 bits");
+ }
+
+ if (k64Cnt > 4)
+ {
+ throw new ArgumentException("Key size larger than 256 bits");
+ }
+
+ /*
+ * k64Cnt is the number of 8 byte blocks (64 chunks)
+ * that are in the input key. The input key is a
+ * maximum of 32 bytes ( 256 bits ), so the range
+ * for k64Cnt is 1..4
+ */
+ for (int i=0,p=0; i<k64Cnt ; i++)
+ {
+ p = i* 8;
+
+ k32e[i] = BytesTo32Bits(key, p);
+ k32o[i] = BytesTo32Bits(key, p+4);
+
+ sBoxKeys[k64Cnt-1-i] = RS_MDS_Encode(k32e[i], k32o[i]);
+ }
+
+ int q,A,B;
+ for (int i=0; i < TOTAL_SUBKEYS / 2 ; i++)
+ {
+ q = i*SK_STEP;
+ A = F32(q, k32e);
+ B = F32(q+SK_BUMP, k32o);
+ B = B << 8 | (int)((uint)B >> 24);
+ A += B;
+ gSubKeys[i*2] = A;
+ A += B;
+ gSubKeys[i*2 + 1] = A << SK_ROTL | (int)((uint)A >> (32-SK_ROTL));
+ }
+
+ /*
+ * fully expand the table for speed
+ */
+ int k0 = sBoxKeys[0];
+ int k1 = sBoxKeys[1];
+ int k2 = sBoxKeys[2];
+ int k3 = sBoxKeys[3];
+ int b0, b1, b2, b3;
+ gSBox = new int[4*MAX_KEY_BITS];
+ for (int i=0; i<MAX_KEY_BITS; i++)
+ {
+ b0 = b1 = b2 = b3 = i;
+ switch (k64Cnt & 3)
+ {
+ case 1:
+ gSBox[i*2] = gMDS0[(P[P_01,b0] & 0xff) ^ M_b0(k0)];
+ gSBox[i*2+1] = gMDS1[(P[P_11,b1] & 0xff) ^ M_b1(k0)];
+ gSBox[i*2+0x200] = gMDS2[(P[P_21,b2] & 0xff) ^ M_b2(k0)];
+ gSBox[i*2+0x201] = gMDS3[(P[P_31,b3] & 0xff) ^ M_b3(k0)];
+ break;
+ case 0: // 256 bits of key
+ b0 = (P[P_04,b0] & 0xff) ^ M_b0(k3);
+ b1 = (P[P_14,b1] & 0xff) ^ M_b1(k3);
+ b2 = (P[P_24,b2] & 0xff) ^ M_b2(k3);
+ b3 = (P[P_34,b3] & 0xff) ^ M_b3(k3);
+ // fall through, having pre-processed b[0]..b[3] with k32[3]
+ goto case 3;
+ case 3: // 192 bits of key
+ b0 = (P[P_03,b0] & 0xff) ^ M_b0(k2);
+ b1 = (P[P_13,b1] & 0xff) ^ M_b1(k2);
+ b2 = (P[P_23,b2] & 0xff) ^ M_b2(k2);
+ b3 = (P[P_33,b3] & 0xff) ^ M_b3(k2);
+ // fall through, having pre-processed b[0]..b[3] with k32[2]
+ goto case 2;
+ case 2: // 128 bits of key
+ gSBox[i * 2] = gMDS0[(P[P_01, (P[P_02, b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)];
+ gSBox[i*2+1] = gMDS1[(P[P_11,(P[P_12,b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)];
+ gSBox[i*2+0x200] = gMDS2[(P[P_21,(P[P_22,b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)];
+ gSBox[i * 2 + 0x201] = gMDS3[(P[P_31, (P[P_32, b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)];
+ break;
+ }
+ }
+
+ /*
+ * the function exits having setup the gSBox with the
+ * input key material.
+ */
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ *
+ * encryptBlock uses the pre-calculated gSBox[] and subKey[]
+ * arrays.
+ */
+ private void EncryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int x0 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
+ int x1 = BytesTo32Bits(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
+ int x2 = BytesTo32Bits(src, srcIndex + 8) ^ gSubKeys[INPUT_WHITEN + 2];
+ int x3 = BytesTo32Bits(src, srcIndex + 12) ^ gSubKeys[INPUT_WHITEN + 3];
+
+ int k = ROUND_SUBKEYS;
+ int t0, t1;
+ for (int r = 0; r < ROUNDS; r +=2)
+ {
+ t0 = Fe32_0(x0);
+ t1 = Fe32_3(x1);
+ x2 ^= t0 + t1 + gSubKeys[k++];
+ x2 = (int)((uint)x2 >>1) | x2 << 31;
+ x3 = (x3 << 1 | (int) ((uint)x3 >> 31)) ^ (t0 + 2*t1 + gSubKeys[k++]);
+
+ t0 = Fe32_0(x2);
+ t1 = Fe32_3(x3);
+ x0 ^= t0 + t1 + gSubKeys[k++];
+ x0 = (int) ((uint)x0 >>1) | x0 << 31;
+ x1 = (x1 << 1 | (int)((uint)x1 >> 31)) ^ (t0 + 2*t1 + gSubKeys[k++]);
+ }
+
+ Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex);
+ Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4);
+ Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8);
+ Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12);
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ */
+ private void DecryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int x2 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
+ int x3 = BytesTo32Bits(src, srcIndex+4) ^ gSubKeys[OUTPUT_WHITEN + 1];
+ int x0 = BytesTo32Bits(src, srcIndex+8) ^ gSubKeys[OUTPUT_WHITEN + 2];
+ int x1 = BytesTo32Bits(src, srcIndex+12) ^ gSubKeys[OUTPUT_WHITEN + 3];
+
+ int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ;
+ int t0, t1;
+ for (int r = 0; r< ROUNDS ; r +=2)
+ {
+ t0 = Fe32_0(x2);
+ t1 = Fe32_3(x3);
+ x1 ^= t0 + 2*t1 + gSubKeys[k--];
+ x0 = (x0 << 1 | (int)((uint) x0 >> 31)) ^ (t0 + t1 + gSubKeys[k--]);
+ x1 = (int) ((uint)x1 >>1) | x1 << 31;
+
+ t0 = Fe32_0(x0);
+ t1 = Fe32_3(x1);
+ x3 ^= t0 + 2*t1 + gSubKeys[k--];
+ x2 = (x2 << 1 | (int)((uint)x2 >> 31)) ^ (t0 + t1 + gSubKeys[k--]);
+ x3 = (int)((uint)x3 >>1) | x3 << 31;
+ }
+
+ Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex);
+ Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4);
+ Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8);
+ Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12);
+ }
+
+ /*
+ * TODO: This can be optimised and made cleaner by combining
+ * the functionality in this function and applying it appropriately
+ * to the creation of the subkeys during key setup.
+ */
+ private int F32(int x, int[] k32)
+ {
+ int b0 = M_b0(x);
+ int b1 = M_b1(x);
+ int b2 = M_b2(x);
+ int b3 = M_b3(x);
+ int k0 = k32[0];
+ int k1 = k32[1];
+ int k2 = k32[2];
+ int k3 = k32[3];
+
+ int result = 0;
+ switch (k64Cnt & 3)
+ {
+ case 1:
+ result = gMDS0[(P[P_01,b0] & 0xff) ^ M_b0(k0)] ^
+ gMDS1[(P[P_11,b1] & 0xff) ^ M_b1(k0)] ^
+ gMDS2[(P[P_21,b2] & 0xff) ^ M_b2(k0)] ^
+ gMDS3[(P[P_31,b3] & 0xff) ^ M_b3(k0)];
+ break;
+ case 0: /* 256 bits of key */
+ b0 = (P[P_04,b0] & 0xff) ^ M_b0(k3);
+ b1 = (P[P_14,b1] & 0xff) ^ M_b1(k3);
+ b2 = (P[P_24,b2] & 0xff) ^ M_b2(k3);
+ b3 = (P[P_34,b3] & 0xff) ^ M_b3(k3);
+ goto case 3;
+ case 3:
+ b0 = (P[P_03,b0] & 0xff) ^ M_b0(k2);
+ b1 = (P[P_13,b1] & 0xff) ^ M_b1(k2);
+ b2 = (P[P_23,b2] & 0xff) ^ M_b2(k2);
+ b3 = (P[P_33,b3] & 0xff) ^ M_b3(k2);
+ goto case 2;
+ case 2:
+ result =
+ gMDS0[(P[P_01,(P[P_02,b0]&0xff)^M_b0(k1)]&0xff)^M_b0(k0)] ^
+ gMDS1[(P[P_11,(P[P_12,b1]&0xff)^M_b1(k1)]&0xff)^M_b1(k0)] ^
+ gMDS2[(P[P_21,(P[P_22,b2]&0xff)^M_b2(k1)]&0xff)^M_b2(k0)] ^
+ gMDS3[(P[P_31,(P[P_32,b3]&0xff)^M_b3(k1)]&0xff)^M_b3(k0)];
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Use (12, 8) Reed-Solomon code over GF(256) to produce
+ * a key S-box 32-bit entity from 2 key material 32-bit
+ * entities.
+ *
+ * @param k0 first 32-bit entity
+ * @param k1 second 32-bit entity
+ * @return Remainder polynomial Generated using RS code
+ */
+ private int RS_MDS_Encode(int k0, int k1)
+ {
+ int r = k1;
+ for (int i = 0 ; i < 4 ; i++) // shift 1 byte at a time
+ {
+ r = RS_rem(r);
+ }
+ r ^= k0;
+ for (int i=0 ; i < 4 ; i++)
+ {
+ r = RS_rem(r);
+ }
+
+ return r;
+ }
+
+ /**
+ * Reed-Solomon code parameters: (12,8) reversible code:
+ * <p>
+ * <pre>
+ * G(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1
+ * </pre>
+ * where a = primitive root of field generator 0x14D
+ * </p>
+ */
+ private int RS_rem(int x)
+ {
+ int b = (int) (((uint)x >> 24) & 0xff);
+ int g2 = ((b << 1) ^
+ ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff;
+ int g3 = ( (int)((uint)b >> 1) ^
+ ((b & 0x01) != 0 ? (int)((uint)RS_GF_FDBK >> 1) : 0)) ^ g2 ;
+ return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b);
+ }
+
+ private int LFSR1(int x)
+ {
+ return (x >> 1) ^
+ (((x & 0x01) != 0) ? GF256_FDBK_2 : 0);
+ }
+
+ private int LFSR2(int x)
+ {
+ return (x >> 2) ^
+ (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^
+ (((x & 0x01) != 0) ? GF256_FDBK_4 : 0);
+ }
+
+ private int Mx_X(int x)
+ {
+ return x ^ LFSR2(x);
+ } // 5B
+
+ private int Mx_Y(int x)
+ {
+ return x ^ LFSR1(x) ^ LFSR2(x);
+ } // EF
+
+ private int M_b0(int x)
+ {
+ return x & 0xff;
+ }
+
+ private int M_b1(int x)
+ {
+ return (int)((uint)x >> 8) & 0xff;
+ }
+
+ private int M_b2(int x)
+ {
+ return (int)((uint)x >> 16) & 0xff;
+ }
+
+ private int M_b3(int x)
+ {
+ return (int)((uint)x >> 24) & 0xff;
+ }
+
+ private int Fe32_0(int x)
+ {
+ return gSBox[ 0x000 + 2*(x & 0xff) ] ^
+ gSBox[ 0x001 + 2*((int)((uint)x >> 8) & 0xff) ] ^
+ gSBox[ 0x200 + 2*((int)((uint)x >> 16) & 0xff) ] ^
+ gSBox[ 0x201 + 2*((int)((uint)x >> 24) & 0xff) ];
+ }
+
+ private int Fe32_3(int x)
+ {
+ return gSBox[ 0x000 + 2*((int)((uint)x >> 24) & 0xff) ] ^
+ gSBox[ 0x001 + 2*(x & 0xff) ] ^
+ gSBox[ 0x200 + 2*((int)((uint)x >> 8) & 0xff) ] ^
+ gSBox[ 0x201 + 2*((int)((uint)x >> 16) & 0xff) ];
+ }
+
+ private int BytesTo32Bits(byte[] b, int p)
+ {
+ return ((b[p] & 0xff) ) |
+ ((b[p+1] & 0xff) << 8) |
+ ((b[p+2] & 0xff) << 16) |
+ ((b[p+3] & 0xff) << 24);
+ }
+
+ private void Bits32ToBytes(int inData, byte[] b, int offset)
+ {
+ b[offset] = (byte)inData;
+ b[offset + 1] = (byte)(inData >> 8);
+ b[offset + 2] = (byte)(inData >> 16);
+ b[offset + 3] = (byte)(inData >> 24);
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/engines/VMPCEngine.cs b/Crypto/src/crypto/engines/VMPCEngine.cs
new file mode 100644
index 000000000..d467fbba5
--- /dev/null
+++ b/Crypto/src/crypto/engines/VMPCEngine.cs
@@ -0,0 +1,139 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ public class VmpcEngine
+ : IStreamCipher
+ {
+ /*
+ * variables to hold the state of the VMPC engine during encryption and
+ * decryption
+ */
+ protected byte n = 0;
+ protected byte[] P = null;
+ protected byte s = 0;
+
+ protected byte[] workingIV;
+ protected byte[] workingKey;
+
+ public virtual string AlgorithmName
+ {
+ get { return "VMPC"; }
+ }
+
+ /**
+ * initialise a VMPC cipher.
+ *
+ * @param forEncryption
+ * whether or not we are for encryption.
+ * @param params
+ * the parameters required to set up the cipher.
+ * @exception ArgumentException
+ * if the params argument is inappropriate.
+ */
+ public virtual void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is ParametersWithIV))
+ throw new ArgumentException("VMPC Init parameters must include an IV");
+
+ ParametersWithIV ivParams = (ParametersWithIV) parameters;
+ KeyParameter key = (KeyParameter) ivParams.Parameters;
+
+ if (!(ivParams.Parameters is KeyParameter))
+ throw new ArgumentException("VMPC Init parameters must include a key");
+
+ this.workingIV = ivParams.GetIV();
+
+ if (workingIV == null || workingIV.Length < 1 || workingIV.Length > 768)
+ throw new ArgumentException("VMPC requires 1 to 768 bytes of IV");
+
+ this.workingKey = key.GetKey();
+
+ InitKey(this.workingKey, this.workingIV);
+ }
+
+ protected virtual void InitKey(
+ byte[] keyBytes,
+ byte[] ivBytes)
+ {
+ s = 0;
+ P = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ P[i] = (byte) i;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.Length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ n = 0;
+ }
+
+ public virtual void ProcessBytes(
+ byte[] input,
+ int inOff,
+ int len,
+ byte[] output,
+ int outOff)
+ {
+ if ((inOff + len) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ // encryption
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+
+ // xor
+ output[i + outOff] = (byte) (input[i + inOff] ^ z);
+ }
+ }
+
+ public virtual void Reset()
+ {
+ InitKey(this.workingKey, this.workingIV);
+ }
+
+ public virtual byte ReturnByte(
+ byte input)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ // encryption
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+
+ // xor
+ return (byte) (input ^ z);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/VMPCKSA3Engine.cs b/Crypto/src/crypto/engines/VMPCKSA3Engine.cs
new file mode 100644
index 000000000..95b6813b7
--- /dev/null
+++ b/Crypto/src/crypto/engines/VMPCKSA3Engine.cs
@@ -0,0 +1,51 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ public class VmpcKsa3Engine
+ : VmpcEngine
+ {
+ public override string AlgorithmName
+ {
+ get { return "VMPC-KSA3"; }
+ }
+
+ protected override void InitKey(
+ byte[] keyBytes,
+ byte[] ivBytes)
+ {
+ s = 0;
+ P = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ P[i] = (byte) i;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.Length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ n = 0;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/engines/XTEAEngine.cs b/Crypto/src/crypto/engines/XTEAEngine.cs
new file mode 100644
index 000000000..eb9291775
--- /dev/null
+++ b/Crypto/src/crypto/engines/XTEAEngine.cs
@@ -0,0 +1,168 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /**
+ * An XTEA engine.
+ */
+ public class XteaEngine
+ : IBlockCipher
+ {
+ private const int
+ rounds = 32,
+ block_size = 8,
+// key_size = 16,
+ delta = unchecked((int) 0x9E3779B9);
+
+ /*
+ * the expanded key array of 4 subkeys
+ */
+ private uint[] _S = new uint[4],
+ _sum0 = new uint[32],
+ _sum1 = new uint[32];
+ private bool _initialised, _forEncryption;
+
+ /**
+ * Create an instance of the TEA encryption algorithm
+ * and set some defaults
+ */
+ public XteaEngine()
+ {
+ _initialised = false;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "XTEA"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ public int GetBlockSize()
+ {
+ return block_size;
+ }
+
+ /**
+ * initialise
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception ArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (!(parameters is KeyParameter))
+ {
+ throw new ArgumentException("invalid parameter passed to TEA init - "
+ + parameters.GetType().FullName);
+ }
+
+ _forEncryption = forEncryption;
+ _initialised = true;
+
+ KeyParameter p = (KeyParameter) parameters;
+
+ setKey(p.GetKey());
+ }
+
+ public int ProcessBlock(
+ byte[] inBytes,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ if (!_initialised)
+ throw new InvalidOperationException(AlgorithmName + " not initialised");
+
+ if ((inOff + block_size) > inBytes.Length)
+ throw new DataLengthException("input buffer too short");
+
+ if ((outOff + block_size) > outBytes.Length)
+ throw new DataLengthException("output buffer too short");
+
+ return _forEncryption
+ ? encryptBlock(inBytes, inOff, outBytes, outOff)
+ : decryptBlock(inBytes, inOff, outBytes, outOff);
+ }
+
+ public void Reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ *
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ int i, j;
+ for (i = j = 0; i < 4; i++,j+=4)
+ {
+ _S[i] = Pack.BE_To_UInt32(key, j);
+ }
+
+ for (i = j = 0; i < rounds; i++)
+ {
+ _sum0[i] = ((uint)j + _S[j & 3]);
+ j += delta;
+ _sum1[i] = ((uint)j + _S[j >> 11 & 3]);
+ }
+ }
+
+ private int encryptBlock(
+ byte[] inBytes,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ // Pack bytes into integers
+ uint v0 = Pack.BE_To_UInt32(inBytes, inOff);
+ uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4);
+
+ for (int i = 0; i < rounds; i++)
+ {
+ v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i];
+ v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i];
+ }
+
+ Pack.UInt32_To_BE(v0, outBytes, outOff);
+ Pack.UInt32_To_BE(v1, outBytes, outOff + 4);
+
+ return block_size;
+ }
+
+ private int decryptBlock(
+ byte[] inBytes,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ // Pack bytes into integers
+ uint v0 = Pack.BE_To_UInt32(inBytes, inOff);
+ uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4);
+
+ for (int i = rounds-1; i >= 0; i--)
+ {
+ v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i];
+ v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i];
+ }
+
+ Pack.UInt32_To_BE(v0, outBytes, outOff);
+ Pack.UInt32_To_BE(v1, outBytes, outOff + 4);
+
+ return block_size;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/BaseKdfBytesGenerator.cs b/Crypto/src/crypto/generators/BaseKdfBytesGenerator.cs
new file mode 100644
index 000000000..0366401d1
--- /dev/null
+++ b/Crypto/src/crypto/generators/BaseKdfBytesGenerator.cs
@@ -0,0 +1,141 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * Basic KDF generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
+ * <br/>
+ * This implementation is based on ISO 18033/P1363a.
+ */
+ public class BaseKdfBytesGenerator
+ : IDerivationFunction
+ {
+ private int counterStart;
+ private IDigest digest;
+ private byte[] shared;
+ private byte[] iv;
+
+ /**
+ * Construct a KDF Parameters generator.
+ *
+ * @param counterStart value of counter.
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ protected BaseKdfBytesGenerator(
+ int counterStart,
+ IDigest digest)
+ {
+ this.counterStart = counterStart;
+ this.digest = digest;
+ }
+
+ public void Init(
+ IDerivationParameters parameters)
+ {
+ if (parameters is KdfParameters)
+ {
+ KdfParameters p = (KdfParameters)parameters;
+
+ shared = p.GetSharedSecret();
+ iv = p.GetIV();
+ }
+ else if (parameters is Iso18033KdfParameters)
+ {
+ Iso18033KdfParameters p = (Iso18033KdfParameters)parameters;
+
+ shared = p.GetSeed();
+ iv = null;
+ }
+ else
+ {
+ throw new ArgumentException("KDF parameters required for KDF Generator");
+ }
+ }
+
+ /**
+ * return the underlying digest.
+ */
+ public IDigest Digest
+ {
+ get
+ {
+ return digest;
+ }
+ }
+
+ /**
+ * fill len bytes of the output buffer with bytes generated from
+ * the derivation function.
+ *
+ * @throws ArgumentException if the size of the request will cause an overflow.
+ * @throws DataLengthException if the out buffer is too small.
+ */
+ public int GenerateBytes(
+ byte[] output,
+ int outOff,
+ int length)
+ {
+ if ((output.Length - length) < outOff)
+ {
+ throw new DataLengthException("output buffer too small");
+ }
+
+ long oBytes = length;
+ int outLen = digest.GetDigestSize();
+
+ //
+ // this is at odds with the standard implementation, the
+ // maximum value should be hBits * (2^32 - 1) where hBits
+ // is the digest output size in bits. We can't have an
+ // array with a long index at the moment...
+ //
+ if (oBytes > ((2L << 32) - 1))
+ {
+ throw new ArgumentException("Output length too large");
+ }
+
+ int cThreshold = (int)((oBytes + outLen - 1) / outLen);
+
+ byte[] dig = new byte[digest.GetDigestSize()];
+
+ int counter = counterStart;
+
+ for (int i = 0; i < cThreshold; i++)
+ {
+ digest.BlockUpdate(shared, 0, shared.Length);
+
+ digest.Update((byte)(counter >> 24));
+ digest.Update((byte)(counter >> 16));
+ digest.Update((byte)(counter >> 8));
+ digest.Update((byte)counter);
+
+ if (iv != null)
+ {
+ digest.BlockUpdate(iv, 0, iv.Length);
+ }
+
+ digest.DoFinal(dig, 0);
+
+ if (length > outLen)
+ {
+ Array.Copy(dig, 0, output, outOff, outLen);
+ outOff += outLen;
+ length -= outLen;
+ }
+ else
+ {
+ Array.Copy(dig, 0, output, outOff, length);
+ }
+
+ counter++;
+ }
+
+ digest.Reset();
+
+ return (int)oBytes;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/generators/DHBasicKeyPairGenerator.cs b/Crypto/src/crypto/generators/DHBasicKeyPairGenerator.cs
new file mode 100644
index 000000000..51b3af687
--- /dev/null
+++ b/Crypto/src/crypto/generators/DHBasicKeyPairGenerator.cs
@@ -0,0 +1,38 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * a basic Diffie-Hellman key pair generator.
+ *
+ * This generates keys consistent for use with the basic algorithm for
+ * Diffie-Hellman.
+ */
+ public class DHBasicKeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+ {
+ private DHKeyGenerationParameters param;
+
+ public virtual void Init(
+ KeyGenerationParameters parameters)
+ {
+ this.param = (DHKeyGenerationParameters)parameters;
+ }
+
+ public virtual AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance;
+ DHParameters dhp = param.Parameters;
+
+ BigInteger x = helper.CalculatePrivate(dhp, param.Random);
+ BigInteger y = helper.CalculatePublic(dhp, x);
+
+ return new AsymmetricCipherKeyPair(
+ new DHPublicKeyParameters(y, dhp),
+ new DHPrivateKeyParameters(x, dhp));
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/DHKeyGeneratorHelper.cs b/Crypto/src/crypto/generators/DHKeyGeneratorHelper.cs
new file mode 100644
index 000000000..756e8482a
--- /dev/null
+++ b/Crypto/src/crypto/generators/DHKeyGeneratorHelper.cs
@@ -0,0 +1,53 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ class DHKeyGeneratorHelper
+ {
+ internal static readonly DHKeyGeneratorHelper Instance = new DHKeyGeneratorHelper();
+
+ private DHKeyGeneratorHelper()
+ {
+ }
+
+ internal BigInteger CalculatePrivate(
+ DHParameters dhParams,
+ SecureRandom random)
+ {
+ int limit = dhParams.L;
+
+ if (limit != 0)
+ {
+ return new BigInteger(limit, random).SetBit(limit - 1);
+ }
+
+ BigInteger min = BigInteger.Two;
+ int m = dhParams.M;
+ if (m != 0)
+ {
+ min = BigInteger.One.ShiftLeft(m - 1);
+ }
+
+ BigInteger max = dhParams.P.Subtract(BigInteger.Two);
+ BigInteger q = dhParams.Q;
+ if (q != null)
+ {
+ max = q.Subtract(BigInteger.Two);
+ }
+
+ return BigIntegers.CreateRandomInRange(min, max, random);
+ }
+
+ internal BigInteger CalculatePublic(
+ DHParameters dhParams,
+ BigInteger x)
+ {
+ return dhParams.G.ModPow(x, dhParams.P);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/DHKeyPairGenerator.cs b/Crypto/src/crypto/generators/DHKeyPairGenerator.cs
new file mode 100644
index 000000000..3bf58ba1b
--- /dev/null
+++ b/Crypto/src/crypto/generators/DHKeyPairGenerator.cs
@@ -0,0 +1,38 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * a Diffie-Hellman key pair generator.
+ *
+ * This generates keys consistent for use in the MTI/A0 key agreement protocol
+ * as described in "Handbook of Applied Cryptography", Pages 516-519.
+ */
+ public class DHKeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+ {
+ private DHKeyGenerationParameters param;
+
+ public virtual void Init(
+ KeyGenerationParameters parameters)
+ {
+ this.param = (DHKeyGenerationParameters)parameters;
+ }
+
+ public virtual AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance;
+ DHParameters dhp = param.Parameters;
+
+ BigInteger x = helper.CalculatePrivate(dhp, param.Random);
+ BigInteger y = helper.CalculatePublic(dhp, x);
+
+ return new AsymmetricCipherKeyPair(
+ new DHPublicKeyParameters(y, dhp),
+ new DHPrivateKeyParameters(x, dhp));
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/DHParametersGenerator.cs b/Crypto/src/crypto/generators/DHParametersGenerator.cs
new file mode 100644
index 000000000..e752c8456
--- /dev/null
+++ b/Crypto/src/crypto/generators/DHParametersGenerator.cs
@@ -0,0 +1,45 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ public class DHParametersGenerator
+ {
+ private int size;
+ private int certainty;
+ private SecureRandom random;
+
+ public virtual void Init(
+ int size,
+ int certainty,
+ SecureRandom random)
+ {
+ this.size = size;
+ this.certainty = certainty;
+ this.random = random;
+ }
+
+ /**
+ * which Generates the p and g values from the given parameters,
+ * returning the DHParameters object.
+ * <p>
+ * Note: can take a while...</p>
+ */
+ public virtual DHParameters GenerateParameters()
+ {
+ //
+ // find a safe prime p where p = 2*q + 1, where p and q are prime.
+ //
+ BigInteger[] safePrimes = DHParametersHelper.GenerateSafePrimes(size, certainty, random);
+
+ BigInteger p = safePrimes[0];
+ BigInteger q = safePrimes[1];
+ BigInteger g = DHParametersHelper.SelectGenerator(p, q, random);
+
+ return new DHParameters(p, g, q, BigInteger.Two, null);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/DHParametersHelper.cs b/Crypto/src/crypto/generators/DHParametersHelper.cs
new file mode 100644
index 000000000..7860cbe33
--- /dev/null
+++ b/Crypto/src/crypto/generators/DHParametersHelper.cs
@@ -0,0 +1,234 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ internal class DHParametersHelper
+ {
+ // The primes b/w 2 and ~2^10
+ /*
+ 3 5 7 11 13 17 19 23 29
+ 31 37 41 43 47 53 59 61 67 71
+ 73 79 83 89 97 101 103 107 109 113
+ 127 131 137 139 149 151 157 163 167 173
+ 179 181 191 193 197 199 211 223 227 229
+ 233 239 241 251 257 263 269 271 277 281
+ 283 293 307 311 313 317 331 337 347 349
+ 353 359 367 373 379 383 389 397 401 409
+ 419 421 431 433 439 443 449 457 461 463
+ 467 479 487 491 499 503 509 521 523 541
+ 547 557 563 569 571 577 587 593 599 601
+ 607 613 617 619 631 641 643 647 653 659
+ 661 673 677 683 691 701 709 719 727 733
+ 739 743 751 757 761 769 773 787 797 809
+ 811 821 823 827 829 839 853 857 859 863
+ 877 881 883 887 907 911 919 929 937 941
+ 947 953 967 971 977 983 991 997
+ 1009 1013 1019 1021 1031
+ */
+
+ // Each list has a product < 2^31
+ private static readonly int[][] primeLists = new int[][]
+ {
+ new int[]{ 3, 5, 7, 11, 13, 17, 19, 23 },
+ new int[]{ 29, 31, 37, 41, 43 },
+ new int[]{ 47, 53, 59, 61, 67 },
+ new int[]{ 71, 73, 79, 83 },
+ new int[]{ 89, 97, 101, 103 },
+
+ new int[]{ 107, 109, 113, 127 },
+ new int[]{ 131, 137, 139, 149 },
+ new int[]{ 151, 157, 163, 167 },
+ new int[]{ 173, 179, 181, 191 },
+ new int[]{ 193, 197, 199, 211 },
+
+ new int[]{ 223, 227, 229 },
+ new int[]{ 233, 239, 241 },
+ new int[]{ 251, 257, 263 },
+ new int[]{ 269, 271, 277 },
+ new int[]{ 281, 283, 293 },
+
+ new int[]{ 307, 311, 313 },
+ new int[]{ 317, 331, 337 },
+ new int[]{ 347, 349, 353 },
+ new int[]{ 359, 367, 373 },
+ new int[]{ 379, 383, 389 },
+
+ new int[]{ 397, 401, 409 },
+ new int[]{ 419, 421, 431 },
+ new int[]{ 433, 439, 443 },
+ new int[]{ 449, 457, 461 },
+ new int[]{ 463, 467, 479 },
+
+ new int[]{ 487, 491, 499 },
+ new int[]{ 503, 509, 521 },
+ new int[]{ 523, 541, 547 },
+ new int[]{ 557, 563, 569 },
+ new int[]{ 571, 577, 587 },
+
+ new int[]{ 593, 599, 601 },
+ new int[]{ 607, 613, 617 },
+ new int[]{ 619, 631, 641 },
+ new int[]{ 643, 647, 653 },
+ new int[]{ 659, 661, 673 },
+
+ new int[]{ 677, 683, 691 },
+ new int[]{ 701, 709, 719 },
+ new int[]{ 727, 733, 739 },
+ new int[]{ 743, 751, 757 },
+ new int[]{ 761, 769, 773 },
+
+ new int[]{ 787, 797, 809 },
+ new int[]{ 811, 821, 823 },
+ new int[]{ 827, 829, 839 },
+ new int[]{ 853, 857, 859 },
+ new int[]{ 863, 877, 881 },
+
+ new int[]{ 883, 887, 907 },
+ new int[]{ 911, 919, 929 },
+ new int[]{ 937, 941, 947 },
+ new int[]{ 953, 967, 971 },
+ new int[]{ 977, 983, 991 },
+
+ new int[]{ 997, 1009, 1013 },
+ new int[]{ 1019, 1021, 1031 },
+ };
+
+ private static readonly BigInteger Six = BigInteger.ValueOf(6);
+
+ private static readonly int[] primeProducts;
+ private static readonly BigInteger[] PrimeProducts;
+
+ static DHParametersHelper()
+ {
+ primeProducts = new int[primeLists.Length];
+ PrimeProducts = new BigInteger[primeLists.Length];
+
+ for (int i = 0; i < primeLists.Length; ++i)
+ {
+ int[] primeList = primeLists[i];
+ int product = 1;
+ for (int j = 0; j < primeList.Length; ++j)
+ {
+ product *= primeList[j];
+ }
+ primeProducts[i] = product;
+ PrimeProducts[i] = BigInteger.ValueOf(product);
+ }
+ }
+
+ /*
+ * Finds a pair of prime BigInteger's {p, q: p = 2q + 1}
+ *
+ * (see: Handbook of Applied Cryptography 4.86)
+ */
+ internal static BigInteger[] GenerateSafePrimes(int size, int certainty, SecureRandom random)
+ {
+ BigInteger p, q;
+ int qLength = size - 1;
+
+ if (size <= 32)
+ {
+ for (;;)
+ {
+ q = new BigInteger(qLength, 2, random);
+
+ p = q.ShiftLeft(1).Add(BigInteger.One);
+
+ if (p.IsProbablePrime(certainty)
+ && (certainty <= 2 || q.IsProbablePrime(certainty)))
+ break;
+ }
+ }
+ else
+ {
+ // Note: Modified from Java version for speed
+ for (;;)
+ {
+ q = new BigInteger(qLength, 0, random);
+
+ retry:
+ for (int i = 0; i < primeLists.Length; ++i)
+ {
+ int test = q.Remainder(PrimeProducts[i]).IntValue;
+
+ if (i == 0)
+ {
+ int rem3 = test % 3;
+ if (rem3 != 2)
+ {
+ int diff = 2 * rem3 + 2;
+ q = q.Add(BigInteger.ValueOf(diff));
+ test = (test + diff) % primeProducts[i];
+ }
+ }
+
+ int[] primeList = primeLists[i];
+ for (int j = 0; j < primeList.Length; ++j)
+ {
+ int prime = primeList[j];
+ int qRem = test % prime;
+ if (qRem == 0 || qRem == (prime >> 1))
+ {
+ q = q.Add(Six);
+ goto retry;
+ }
+ }
+ }
+
+
+ if (q.BitLength != qLength)
+ continue;
+
+ if (!q.RabinMillerTest(2, random))
+ continue;
+
+ p = q.ShiftLeft(1).Add(BigInteger.One);
+
+ if (p.RabinMillerTest(certainty, random)
+ && (certainty <= 2 || q.RabinMillerTest(certainty - 2, random)))
+ break;
+ }
+ }
+
+ return new BigInteger[] { p, q };
+ }
+
+ /*
+ * Select a high order element of the multiplicative group Zp*
+ *
+ * p and q must be s.t. p = 2*q + 1, where p and q are prime (see generateSafePrimes)
+ */
+ internal static BigInteger SelectGenerator(BigInteger p, BigInteger q, SecureRandom random)
+ {
+ BigInteger pMinusTwo = p.Subtract(BigInteger.Two);
+ BigInteger g;
+
+ /*
+ * (see: Handbook of Applied Cryptography 4.80)
+ */
+// do
+// {
+// g = BigIntegers.CreateRandomInRange(BigInteger.Two, pMinusTwo, random);
+// }
+// while (g.ModPow(BigInteger.Two, p).Equals(BigInteger.One)
+// || g.ModPow(q, p).Equals(BigInteger.One));
+
+ /*
+ * RFC 2631 2.2.1.2 (and see: Handbook of Applied Cryptography 4.81)
+ */
+ do
+ {
+ BigInteger h = BigIntegers.CreateRandomInRange(BigInteger.Two, pMinusTwo, random);
+
+ g = h.ModPow(BigInteger.Two, p);
+ }
+ while (g.Equals(BigInteger.One));
+
+ return g;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/DesEdeKeyGenerator.cs b/Crypto/src/crypto/generators/DesEdeKeyGenerator.cs
new file mode 100644
index 000000000..5902643fd
--- /dev/null
+++ b/Crypto/src/crypto/generators/DesEdeKeyGenerator.cs
@@ -0,0 +1,67 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ public class DesEdeKeyGenerator
+ : DesKeyGenerator
+ {
+ public DesEdeKeyGenerator()
+ {
+ }
+
+ internal DesEdeKeyGenerator(
+ int defaultStrength)
+ : base(defaultStrength)
+ {
+ }
+
+ /**
+ * initialise the key generator - if strength is set to zero
+ * the key Generated will be 192 bits in size, otherwise
+ * strength can be 128 or 192 (or 112 or 168 if you don't count
+ * parity bits), depending on whether you wish to do 2-key or 3-key
+ * triple DES.
+ *
+ * @param param the parameters to be used for key generation
+ */
+ protected override void engineInit(
+ KeyGenerationParameters parameters)
+ {
+ this.random = parameters.Random;
+ this.strength = (parameters.Strength + 7) / 8;
+
+ if (strength == 0 || strength == (168 / 8))
+ {
+ strength = DesEdeParameters.DesEdeKeyLength;
+ }
+ else if (strength == (112 / 8))
+ {
+ strength = 2 * DesEdeParameters.DesKeyLength;
+ }
+ else if (strength != DesEdeParameters.DesEdeKeyLength
+ && strength != (2 * DesEdeParameters.DesKeyLength))
+ {
+ throw new ArgumentException("DESede key must be "
+ + (DesEdeParameters.DesEdeKeyLength * 8) + " or "
+ + (2 * 8 * DesEdeParameters.DesKeyLength)
+ + " bits long.");
+ }
+ }
+
+ protected override byte[] engineGenerateKey()
+ {
+ byte[] newKey;
+
+ do
+ {
+ newKey = random.GenerateSeed(strength);
+ DesEdeParameters.SetOddParity(newKey);
+ }
+ while (DesEdeParameters.IsWeakKey(newKey, 0, newKey.Length));
+
+ return newKey;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/DesKeyGenerator.cs b/Crypto/src/crypto/generators/DesKeyGenerator.cs
new file mode 100644
index 000000000..154e3471a
--- /dev/null
+++ b/Crypto/src/crypto/generators/DesKeyGenerator.cs
@@ -0,0 +1,57 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ public class DesKeyGenerator
+ : CipherKeyGenerator
+ {
+ public DesKeyGenerator()
+ {
+ }
+
+ internal DesKeyGenerator(
+ int defaultStrength)
+ : base(defaultStrength)
+ {
+ }
+
+ /**
+ * initialise the key generator - if strength is set to zero
+ * the key generated will be 64 bits in size, otherwise
+ * strength can be 64 or 56 bits (if you don't count the parity bits).
+ *
+ * @param param the parameters to be used for key generation
+ */
+ protected override void engineInit(
+ KeyGenerationParameters parameters)
+ {
+ base.engineInit(parameters);
+
+ if (strength == 0 || strength == (56 / 8))
+ {
+ strength = DesParameters.DesKeyLength;
+ }
+ else if (strength != DesParameters.DesKeyLength)
+ {
+ throw new ArgumentException(
+ "DES key must be " + (DesParameters.DesKeyLength * 8) + " bits long.");
+ }
+ }
+
+ protected override byte[] engineGenerateKey()
+ {
+ byte[] newKey;
+
+ do
+ {
+ newKey = random.GenerateSeed(DesParameters.DesKeyLength);
+ DesParameters.SetOddParity(newKey);
+ }
+ while (DesParameters.IsWeakKey(newKey, 0));
+
+ return newKey;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/DsaKeyPairGenerator.cs b/Crypto/src/crypto/generators/DsaKeyPairGenerator.cs
new file mode 100644
index 000000000..bb8ec591b
--- /dev/null
+++ b/Crypto/src/crypto/generators/DsaKeyPairGenerator.cs
@@ -0,0 +1,61 @@
+using System;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * a DSA key pair generator.
+ *
+ * This Generates DSA keys in line with the method described
+ * in <i>FIPS 186-3 B.1 FFC Key Pair Generation</i>.
+ */
+ public class DsaKeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+ {
+ private DsaKeyGenerationParameters param;
+
+ public void Init(
+ KeyGenerationParameters parameters)
+ {
+ if (parameters == null)
+ throw new ArgumentNullException("parameters");
+
+ // Note: If we start accepting instances of KeyGenerationParameters,
+ // must apply constraint checking on strength (see DsaParametersGenerator.Init)
+
+ this.param = (DsaKeyGenerationParameters) parameters;
+ }
+
+ public AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ DsaParameters dsaParams = param.Parameters;
+
+ BigInteger x = GeneratePrivateKey(dsaParams.Q, param.Random);
+ BigInteger y = CalculatePublicKey(dsaParams.P, dsaParams.G, x);
+
+ return new AsymmetricCipherKeyPair(
+ new DsaPublicKeyParameters(y, dsaParams),
+ new DsaPrivateKeyParameters(x, dsaParams));
+ }
+
+ private static BigInteger GeneratePrivateKey(BigInteger q, SecureRandom random)
+ {
+ // TODO Prefer this method? (change test cases that used fixed random)
+ // B.1.1 Key Pair Generation Using Extra Random Bits
+// BigInteger c = new BigInteger(q.BitLength + 64, random);
+// return c.Mod(q.Subtract(BigInteger.One)).Add(BigInteger.One);
+
+ // B.1.2 Key Pair Generation by Testing Candidates
+ return BigIntegers.CreateRandomInRange(BigInteger.One, q.Subtract(BigInteger.One), random);
+ }
+
+ private static BigInteger CalculatePublicKey(BigInteger p, BigInteger g, BigInteger x)
+ {
+ return g.ModPow(x, p);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/DsaParametersGenerator.cs b/Crypto/src/crypto/generators/DsaParametersGenerator.cs
new file mode 100644
index 000000000..3e9d4f021
--- /dev/null
+++ b/Crypto/src/crypto/generators/DsaParametersGenerator.cs
@@ -0,0 +1,355 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ // TODO Update docs to mention FIPS 186-3 when done
+ /**
+ * Generate suitable parameters for DSA, in line with FIPS 186-2.
+ */
+ public class DsaParametersGenerator
+ {
+ private int L, N;
+ private int certainty;
+ private SecureRandom random;
+
+ /**
+ * initialise the key generator.
+ *
+ * @param size size of the key (range 2^512 -> 2^1024 - 64 bit increments)
+ * @param certainty measure of robustness of prime (for FIPS 186-2 compliance this should be at least 80).
+ * @param random random byte source.
+ */
+ public void Init(
+ int size,
+ int certainty,
+ SecureRandom random)
+ {
+ if (!IsValidDsaStrength(size))
+ throw new ArgumentException("size must be from 512 - 1024 and a multiple of 64", "size");
+
+ Init(size, GetDefaultN(size), certainty, random);
+ }
+
+ // TODO Make public to enable support for DSA keys > 1024 bits
+ private void Init(
+ int L,
+ int N,
+ int certainty,
+ SecureRandom random)
+ {
+ // TODO Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2)
+ // TODO Should we enforce the minimum 'certainty' values as per C.3 Table C.1?
+
+ this.L = L;
+ this.N = N;
+ this.certainty = certainty;
+ this.random = random;
+ }
+
+// /**
+// * add value to b, returning the result in a. The a value is treated
+// * as a BigInteger of length (a.Length * 8) bits. The result is
+// * modulo 2^a.Length in case of overflow.
+// */
+// private static void Add(
+// byte[] a,
+// byte[] b,
+// int value)
+// {
+// int x = (b[b.Length - 1] & 0xff) + value;
+//
+// a[b.Length - 1] = (byte)x;
+// x = (int) ((uint) x >>8);
+//
+// for (int i = b.Length - 2; i >= 0; i--)
+// {
+// x += (b[i] & 0xff);
+// a[i] = (byte)x;
+// x = (int) ((uint) x >>8);
+// }
+// }
+
+ /**
+ * which Generates the p and g values from the given parameters,
+ * returning the DsaParameters object.
+ * <p>
+ * Note: can take a while...</p>
+ */
+ public DsaParameters GenerateParameters()
+ {
+ return L > 1024
+ ? GenerateParameters_FIPS186_3()
+ : GenerateParameters_FIPS186_2();
+ }
+
+ private DsaParameters GenerateParameters_FIPS186_2()
+ {
+ byte[] seed = new byte[20];
+ byte[] part1 = new byte[20];
+ byte[] part2 = new byte[20];
+ byte[] u = new byte[20];
+ Sha1Digest sha1 = new Sha1Digest();
+ int n = (L - 1) / 160;
+ byte[] w = new byte[L / 8];
+
+ for (;;)
+ {
+ random.NextBytes(seed);
+
+ Hash(sha1, seed, part1);
+ Array.Copy(seed, 0, part2, 0, seed.Length);
+ Inc(part2);
+ Hash(sha1, part2, part2);
+
+ for (int i = 0; i != u.Length; i++)
+ {
+ u[i] = (byte)(part1[i] ^ part2[i]);
+ }
+
+ u[0] |= (byte)0x80;
+ u[19] |= (byte)0x01;
+
+ BigInteger q = new BigInteger(1, u);
+
+ if (!q.IsProbablePrime(certainty))
+ continue;
+
+ byte[] offset = Arrays.Clone(seed);
+ Inc(offset);
+
+ for (int counter = 0; counter < 4096; ++counter)
+ {
+ for (int k = 0; k < n; k++)
+ {
+ Inc(offset);
+ Hash(sha1, offset, part1);
+ Array.Copy(part1, 0, w, w.Length - (k + 1) * part1.Length, part1.Length);
+ }
+
+ Inc(offset);
+ Hash(sha1, offset, part1);
+ Array.Copy(part1, part1.Length - ((w.Length - (n) * part1.Length)), w, 0, w.Length - n * part1.Length);
+
+ w[0] |= (byte)0x80;
+
+ BigInteger x = new BigInteger(1, w);
+
+ BigInteger c = x.Mod(q.ShiftLeft(1));
+
+ BigInteger p = x.Subtract(c.Subtract(BigInteger.One));
+
+ if (p.BitLength != L)
+ continue;
+
+ if (p.IsProbablePrime(certainty))
+ {
+ BigInteger g = CalculateGenerator_FIPS186_2(p, q, random);
+
+ return new DsaParameters(p, q, g, new DsaValidationParameters(seed, counter));
+ }
+ }
+ }
+ }
+
+ private static BigInteger CalculateGenerator_FIPS186_2(BigInteger p, BigInteger q, SecureRandom r)
+ {
+ BigInteger e = p.Subtract(BigInteger.One).Divide(q);
+ BigInteger pSub2 = p.Subtract(BigInteger.Two);
+
+ for (;;)
+ {
+ BigInteger h = BigIntegers.CreateRandomInRange(BigInteger.Two, pSub2, r);
+ BigInteger g = h.ModPow(e, p);
+
+ if (g.BitLength > 1)
+ return g;
+ }
+ }
+
+ /**
+ * generate suitable parameters for DSA, in line with
+ * <i>FIPS 186-3 A.1 Generation of the FFC Primes p and q</i>.
+ */
+ private DsaParameters GenerateParameters_FIPS186_3()
+ {
+// A.1.1.2 Generation of the Probable Primes p and q Using an Approved Hash Function
+ // FIXME This should be configurable (digest size in bits must be >= N)
+ IDigest d = new Sha256Digest();
+ int outlen = d.GetDigestSize() * 8;
+
+// 1. Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2). If
+// the pair is not in the list, then return INVALID.
+ // Note: checked at initialisation
+
+// 2. If (seedlen < N), then return INVALID.
+ // FIXME This should be configurable (must be >= N)
+ int seedlen = N;
+ byte[] seed = new byte[seedlen / 8];
+
+// 3. n = ceiling(L ⁄ outlen) – 1.
+ int n = (L - 1) / outlen;
+
+// 4. b = L – 1 – (n ∗ outlen).
+ int b = (L - 1) % outlen;
+
+ byte[] output = new byte[d.GetDigestSize()];
+ for (;;)
+ {
+// 5. Get an arbitrary sequence of seedlen bits as the domain_parameter_seed.
+ random.NextBytes(seed);
+
+// 6. U = Hash (domain_parameter_seed) mod 2^(N–1).
+ Hash(d, seed, output);
+ BigInteger U = new BigInteger(1, output).Mod(BigInteger.One.ShiftLeft(N - 1));
+
+// 7. q = 2^(N–1) + U + 1 – ( U mod 2).
+ BigInteger q = BigInteger.One.ShiftLeft(N - 1).Add(U).Add(BigInteger.One).Subtract(
+ U.Mod(BigInteger.Two));
+
+// 8. Test whether or not q is prime as specified in Appendix C.3.
+ // TODO Review C.3 for primality checking
+ if (!q.IsProbablePrime(certainty))
+ {
+// 9. If q is not a prime, then go to step 5.
+ continue;
+ }
+
+// 10. offset = 1.
+ // Note: 'offset' value managed incrementally
+ byte[] offset = Arrays.Clone(seed);
+
+// 11. For counter = 0 to (4L – 1) do
+ int counterLimit = 4 * L;
+ for (int counter = 0; counter < counterLimit; ++counter)
+ {
+// 11.1 For j = 0 to n do
+// Vj = Hash ((domain_parameter_seed + offset + j) mod 2^seedlen).
+// 11.2 W = V0 + (V1 ∗ 2^outlen) + ... + (V^(n–1) ∗ 2^((n–1) ∗ outlen)) + ((Vn mod 2^b) ∗ 2^(n ∗ outlen)).
+ // TODO Assemble w as a byte array
+ BigInteger W = BigInteger.Zero;
+ for (int j = 0, exp = 0; j <= n; ++j, exp += outlen)
+ {
+ Inc(offset);
+ Hash(d, offset, output);
+
+ BigInteger Vj = new BigInteger(1, output);
+ if (j == n)
+ {
+ Vj = Vj.Mod(BigInteger.One.ShiftLeft(b));
+ }
+
+ W = W.Add(Vj.ShiftLeft(exp));
+ }
+
+// 11.3 X = W + 2^(L–1). Comment: 0 ≤ W < 2L–1; hence, 2L–1 ≤ X < 2L.
+ BigInteger X = W.Add(BigInteger.One.ShiftLeft(L - 1));
+
+// 11.4 c = X mod 2q.
+ BigInteger c = X.Mod(q.ShiftLeft(1));
+
+// 11.5 p = X - (c - 1). Comment: p ≡ 1 (mod 2q).
+ BigInteger p = X.Subtract(c.Subtract(BigInteger.One));
+
+ // 11.6 If (p < 2^(L - 1)), then go to step 11.9
+ if (p.BitLength != L)
+ continue;
+
+// 11.7 Test whether or not p is prime as specified in Appendix C.3.
+ // TODO Review C.3 for primality checking
+ if (p.IsProbablePrime(certainty))
+ {
+// 11.8 If p is determined to be prime, then return VALID and the values of p, q and
+// (optionally) the values of domain_parameter_seed and counter.
+ // TODO Make configurable (8-bit unsigned)?
+// int index = 1;
+// BigInteger g = CalculateGenerator_FIPS186_3_Verifiable(d, p, q, seed, index);
+// if (g != null)
+// {
+// // TODO Should 'index' be a part of the validation parameters?
+// return new DsaParameters(p, q, g, new DsaValidationParameters(seed, counter));
+// }
+
+ BigInteger g = CalculateGenerator_FIPS186_3_Unverifiable(p, q, random);
+ return new DsaParameters(p, q, g, new DsaValidationParameters(seed, counter));
+ }
+
+// 11.9 offset = offset + n + 1. Comment: Increment offset; then, as part of
+// the loop in step 11, increment counter; if
+// counter < 4L, repeat steps 11.1 through 11.8.
+ // Note: 'offset' value already incremented in inner loop
+ }
+// 12. Go to step 5.
+ }
+ }
+
+ private static BigInteger CalculateGenerator_FIPS186_3_Unverifiable(BigInteger p, BigInteger q,
+ SecureRandom r)
+ {
+ return CalculateGenerator_FIPS186_2(p, q, r);
+ }
+
+ private static BigInteger CalculateGenerator_FIPS186_3_Verifiable(IDigest d, BigInteger p, BigInteger q,
+ byte[] seed, int index)
+ {
+ // A.2.3 Verifiable Canonical Generation of the Generator g
+ BigInteger e = p.Subtract(BigInteger.One).Divide(q);
+ byte[] ggen = Hex.Decode("6767656E");
+
+ // 7. U = domain_parameter_seed || "ggen" || index || count.
+ byte[] U = new byte[seed.Length + ggen.Length + 1 + 2];
+ Array.Copy(seed, 0, U, 0, seed.Length);
+ Array.Copy(ggen, 0, U, seed.Length, ggen.Length);
+ U[U.Length - 3] = (byte)index;
+
+ byte[] w = new byte[d.GetDigestSize()];
+ for (int count = 1; count < (1 << 16); ++count)
+ {
+ Inc(U);
+ Hash(d, U, w);
+ BigInteger W = new BigInteger(1, w);
+ BigInteger g = W.ModPow(e, p);
+
+ if (g.CompareTo(BigInteger.Two) >= 0)
+ return g;
+ }
+
+ return null;
+ }
+
+ private static bool IsValidDsaStrength(
+ int strength)
+ {
+ return strength >= 512 && strength <= 1024 && strength % 64 == 0;
+ }
+
+ private static void Hash(IDigest d, byte[] input, byte[] output)
+ {
+ d.BlockUpdate(input, 0, input.Length);
+ d.DoFinal(output, 0);
+ }
+
+ private static int GetDefaultN(int L)
+ {
+ return L > 1024 ? 256 : 160;
+ }
+
+ private static void Inc(byte[] buf)
+ {
+ for (int i = buf.Length - 1; i >= 0; --i)
+ {
+ byte b = (byte)(buf[i] + 1);
+ buf[i] = b;
+
+ if (b != 0)
+ break;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/ECKeyPairGenerator.cs b/Crypto/src/crypto/generators/ECKeyPairGenerator.cs
new file mode 100644
index 000000000..d1e4b7cf6
--- /dev/null
+++ b/Crypto/src/crypto/generators/ECKeyPairGenerator.cs
@@ -0,0 +1,174 @@
+using System;
+using System.Globalization;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ public class ECKeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+ {
+ private readonly string algorithm;
+
+ private ECDomainParameters parameters;
+ private DerObjectIdentifier publicKeyParamSet;
+ private SecureRandom random;
+
+ public ECKeyPairGenerator()
+ : this("EC")
+ {
+ }
+
+ public ECKeyPairGenerator(
+ string algorithm)
+ {
+ if (algorithm == null)
+ throw new ArgumentNullException("algorithm");
+
+ this.algorithm = VerifyAlgorithmName(algorithm);
+ }
+
+ public void Init(
+ KeyGenerationParameters parameters)
+ {
+ if (parameters is ECKeyGenerationParameters)
+ {
+ ECKeyGenerationParameters ecP = (ECKeyGenerationParameters) parameters;
+
+ this.publicKeyParamSet = ecP.PublicKeyParamSet;
+ this.parameters = ecP.DomainParameters;
+ }
+ else
+ {
+ DerObjectIdentifier oid;
+ switch (parameters.Strength)
+ {
+ case 192:
+ oid = X9ObjectIdentifiers.Prime192v1;
+ break;
+ case 224:
+ oid = SecObjectIdentifiers.SecP224r1;
+ break;
+ case 239:
+ oid = X9ObjectIdentifiers.Prime239v1;
+ break;
+ case 256:
+ oid = X9ObjectIdentifiers.Prime256v1;
+ break;
+ case 384:
+ oid = SecObjectIdentifiers.SecP384r1;
+ break;
+ case 521:
+ oid = SecObjectIdentifiers.SecP521r1;
+ break;
+ default:
+ throw new InvalidParameterException("unknown key size.");
+ }
+
+ X9ECParameters ecps = FindECCurveByOid(oid);
+
+ this.parameters = new ECDomainParameters(
+ ecps.Curve, ecps.G, ecps.N, ecps.H, ecps.GetSeed());
+ }
+
+ this.random = parameters.Random;
+ }
+
+ /**
+ * Given the domain parameters this routine Generates an EC key
+ * pair in accordance with X9.62 section 5.2.1 pages 26, 27.
+ */
+ public AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ BigInteger n = parameters.N;
+ BigInteger d;
+
+ do
+ {
+ d = new BigInteger(n.BitLength, random);
+ }
+ while (d.SignValue == 0 || (d.CompareTo(n) >= 0));
+
+ ECPoint q = parameters.G.Multiply(d);
+
+ if (publicKeyParamSet != null)
+ {
+ return new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(algorithm, q, publicKeyParamSet),
+ new ECPrivateKeyParameters(algorithm, d, publicKeyParamSet));
+ }
+
+ return new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(algorithm, q, parameters),
+ new ECPrivateKeyParameters(algorithm, d, parameters));
+ }
+
+ private string VerifyAlgorithmName(
+ string algorithm)
+ {
+ string upper = algorithm.ToUpperInvariant();
+
+ switch (upper)
+ {
+ case "EC":
+ case "ECDSA":
+ case "ECDH":
+ case "ECDHC":
+ case "ECGOST3410":
+ case "ECMQV":
+ break;
+ default:
+ throw new ArgumentException("unrecognised algorithm: " + algorithm, "algorithm");
+ }
+
+ return upper;
+ }
+
+ internal static X9ECParameters FindECCurveByOid(DerObjectIdentifier oid)
+ {
+ // TODO ECGost3410NamedCurves support (returns ECDomainParameters though)
+
+ X9ECParameters ecP = X962NamedCurves.GetByOid(oid);
+
+ if (ecP == null)
+ {
+ ecP = SecNamedCurves.GetByOid(oid);
+
+ if (ecP == null)
+ {
+ ecP = NistNamedCurves.GetByOid(oid);
+
+ if (ecP == null)
+ {
+ ecP = TeleTrusTNamedCurves.GetByOid(oid);
+ }
+ }
+ }
+
+ return ecP;
+ }
+
+ internal static ECPublicKeyParameters GetCorrespondingPublicKey(
+ ECPrivateKeyParameters privKey)
+ {
+ ECDomainParameters parameters = privKey.Parameters;
+ ECPoint q = parameters.G.Multiply(privKey.D);
+
+ if (privKey.PublicKeyParamSet != null)
+ {
+ return new ECPublicKeyParameters(privKey.AlgorithmName, q, privKey.PublicKeyParamSet);
+ }
+
+ return new ECPublicKeyParameters(privKey.AlgorithmName, q, parameters);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/ElGamalKeyPairGenerator.cs b/Crypto/src/crypto/generators/ElGamalKeyPairGenerator.cs
new file mode 100644
index 000000000..227e7fe94
--- /dev/null
+++ b/Crypto/src/crypto/generators/ElGamalKeyPairGenerator.cs
@@ -0,0 +1,40 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * a ElGamal key pair generator.
+ * <p>
+ * This Generates keys consistent for use with ElGamal as described in
+ * page 164 of "Handbook of Applied Cryptography".</p>
+ */
+ public class ElGamalKeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+ {
+ private ElGamalKeyGenerationParameters param;
+
+ public void Init(
+ KeyGenerationParameters parameters)
+ {
+ this.param = (ElGamalKeyGenerationParameters) parameters;
+ }
+
+ public AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance;
+ ElGamalParameters egp = param.Parameters;
+ DHParameters dhp = new DHParameters(egp.P, egp.G, null, 0, egp.L);
+
+ BigInteger x = helper.CalculatePrivate(dhp, param.Random);
+ BigInteger y = helper.CalculatePublic(dhp, x);
+
+ return new AsymmetricCipherKeyPair(
+ new ElGamalPublicKeyParameters(y, egp),
+ new ElGamalPrivateKeyParameters(x, egp));
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/generators/ElGamalParametersGenerator.cs b/Crypto/src/crypto/generators/ElGamalParametersGenerator.cs
new file mode 100644
index 000000000..8443bb00e
--- /dev/null
+++ b/Crypto/src/crypto/generators/ElGamalParametersGenerator.cs
@@ -0,0 +1,46 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ public class ElGamalParametersGenerator
+ {
+ private int size;
+ private int certainty;
+ private SecureRandom random;
+
+ public void Init(
+ int size,
+ int certainty,
+ SecureRandom random)
+ {
+ this.size = size;
+ this.certainty = certainty;
+ this.random = random;
+ }
+
+ /**
+ * which Generates the p and g values from the given parameters,
+ * returning the ElGamalParameters object.
+ * <p>
+ * Note: can take a while...
+ * </p>
+ */
+ public ElGamalParameters GenerateParameters()
+ {
+ //
+ // find a safe prime p where p = 2*q + 1, where p and q are prime.
+ //
+ BigInteger[] safePrimes = DHParametersHelper.GenerateSafePrimes(size, certainty, random);
+
+ BigInteger p = safePrimes[0];
+ BigInteger q = safePrimes[1];
+ BigInteger g = DHParametersHelper.SelectGenerator(p, q, random);
+
+ return new ElGamalParameters(p, g);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs b/Crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs
new file mode 100644
index 000000000..5878da64b
--- /dev/null
+++ b/Crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs
@@ -0,0 +1,73 @@
+using System;
+
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * a GOST3410 key pair generator.
+ * This generates GOST3410 keys in line with the method described
+ * in GOST R 34.10-94.
+ */
+ public class Gost3410KeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+ {
+ private Gost3410KeyGenerationParameters param;
+
+ public void Init(
+ KeyGenerationParameters parameters)
+ {
+ if (parameters is Gost3410KeyGenerationParameters)
+ {
+ this.param = (Gost3410KeyGenerationParameters) parameters;
+ }
+ else
+ {
+ Gost3410KeyGenerationParameters kgp = new Gost3410KeyGenerationParameters(
+ parameters.Random,
+ CryptoProObjectIdentifiers.GostR3410x94CryptoProA);
+
+ if (parameters.Strength != kgp.Parameters.P.BitLength - 1)
+ {
+ // TODO Should we complain?
+ }
+
+ this.param = kgp;
+ }
+ }
+
+ public AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ SecureRandom random = param.Random;
+ Gost3410Parameters gost3410Params = param.Parameters;
+
+ BigInteger q = gost3410Params.Q;
+ BigInteger x;
+ do
+ {
+ x = new BigInteger(256, random);
+ }
+ while (x.SignValue < 1 || x.CompareTo(q) >= 0);
+
+ BigInteger p = gost3410Params.P;
+ BigInteger a = gost3410Params.A;
+
+ // calculate the public key.
+ BigInteger y = a.ModPow(x, p);
+
+ if (param.PublicKeyParamSet != null)
+ {
+ return new AsymmetricCipherKeyPair(
+ new Gost3410PublicKeyParameters(y, param.PublicKeyParamSet),
+ new Gost3410PrivateKeyParameters(x, param.PublicKeyParamSet));
+ }
+
+ return new AsymmetricCipherKeyPair(
+ new Gost3410PublicKeyParameters(y, gost3410Params),
+ new Gost3410PrivateKeyParameters(x, gost3410Params));
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/GOST3410ParametersGenerator.cs b/Crypto/src/crypto/generators/GOST3410ParametersGenerator.cs
new file mode 100644
index 000000000..52a9f5a82
--- /dev/null
+++ b/Crypto/src/crypto/generators/GOST3410ParametersGenerator.cs
@@ -0,0 +1,530 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * generate suitable parameters for GOST3410.
+ */
+ public class Gost3410ParametersGenerator
+ {
+ private int size;
+ private int typeproc;
+ private SecureRandom init_random;
+
+ /**
+ * initialise the key generator.
+ *
+ * @param size size of the key
+ * @param typeProcedure type procedure A,B = 1; A',B' - else
+ * @param random random byte source.
+ */
+ public void Init(
+ int size,
+ int typeProcedure,
+ SecureRandom random)
+ {
+ this.size = size;
+ this.typeproc = typeProcedure;
+ this.init_random = random;
+ }
+
+ //Procedure A
+ private int procedure_A(int x0, int c, BigInteger[] pq, int size)
+ {
+ //Verify and perform condition: 0<x<2^16; 0<c<2^16; c - odd.
+ while(x0<0 || x0>65536)
+ {
+ x0 = init_random.NextInt()/32768;
+ }
+
+ while((c<0 || c>65536) || (c/2==0))
+ {
+ c = init_random.NextInt()/32768 + 1;
+ }
+
+ BigInteger C = BigInteger.ValueOf(c);
+ BigInteger constA16 = BigInteger.ValueOf(19381);
+
+ //step1
+ BigInteger[] y = new BigInteger[1]; // begin length = 1
+ y[0] = BigInteger.ValueOf(x0);
+
+ //step 2
+ int[] t = new int[1]; // t - orders; begin length = 1
+ t[0] = size;
+ int s = 0;
+ for (int i=0; t[i]>=17; i++)
+ {
+ // extension array t
+ int[] tmp_t = new int[t.Length + 1]; ///////////////
+ Array.Copy(t,0,tmp_t,0,t.Length); // extension
+ t = new int[tmp_t.Length]; // array t
+ Array.Copy(tmp_t, 0, t, 0, tmp_t.Length); ///////////////
+
+ t[i+1] = t[i]/2;
+ s = i+1;
+ }
+
+ //step3
+ BigInteger[] p = new BigInteger[s+1];
+ p[s] = new BigInteger("8003",16); //set min prime number length 16 bit
+
+ int m = s-1; //step4
+
+ for (int i=0; i<s; i++)
+ {
+ int rm = t[m]/16; //step5
+
+ step6: for(;;)
+ {
+ //step 6
+ BigInteger[] tmp_y = new BigInteger[y.Length]; ////////////////
+ Array.Copy(y,0,tmp_y,0,y.Length); // extension
+ y = new BigInteger[rm+1]; // array y
+ Array.Copy(tmp_y,0,y,0,tmp_y.Length); ////////////////
+
+ for (int j=0; j<rm; j++)
+ {
+ y[j+1] = (y[j].Multiply(constA16).Add(C)).Mod(BigInteger.Two.Pow(16));
+ }
+
+ //step 7
+ BigInteger Ym = BigInteger.Zero;
+ for (int j=0; j<rm; j++)
+ {
+ Ym = Ym.Add(y[j].ShiftLeft(16*j));
+ }
+
+ y[0] = y[rm]; //step 8
+
+ //step 9
+ BigInteger N = BigInteger.One.ShiftLeft(t[m]-1).Divide(p[m+1]).Add(
+ Ym.ShiftLeft(t[m]-1).Divide(p[m+1].ShiftLeft(16*rm)));
+
+ if (N.TestBit(0))
+ {
+ N = N.Add(BigInteger.One);
+ }
+
+ //step 10
+
+ for(;;)
+ {
+ //step 11
+ BigInteger NByLastP = N.Multiply(p[m+1]);
+
+ if (NByLastP.BitLength > t[m])
+ {
+ goto step6; //step 12
+ }
+
+ p[m] = NByLastP.Add(BigInteger.One);
+
+ //step13
+ if (BigInteger.Two.ModPow(NByLastP, p[m]).CompareTo(BigInteger.One) == 0
+ && BigInteger.Two.ModPow(N, p[m]).CompareTo(BigInteger.One) != 0)
+ {
+ break;
+ }
+
+ N = N.Add(BigInteger.Two);
+ }
+
+ if (--m < 0)
+ {
+ pq[0] = p[0];
+ pq[1] = p[1];
+ return y[0].IntValue; //return for procedure B step 2
+ }
+
+ break; //step 14
+ }
+ }
+ return y[0].IntValue;
+ }
+
+ //Procedure A'
+ private long procedure_Aa(long x0, long c, BigInteger[] pq, int size)
+ {
+ //Verify and perform condition: 0<x<2^32; 0<c<2^32; c - odd.
+ while(x0<0 || x0>4294967296L)
+ {
+ x0 = init_random.NextInt()*2;
+ }
+
+ while((c<0 || c>4294967296L) || (c/2==0))
+ {
+ c = init_random.NextInt()*2+1;
+ }
+
+ BigInteger C = BigInteger.ValueOf(c);
+ BigInteger constA32 = BigInteger.ValueOf(97781173);
+
+ //step1
+ BigInteger[] y = new BigInteger[1]; // begin length = 1
+ y[0] = BigInteger.ValueOf(x0);
+
+ //step 2
+ int[] t = new int[1]; // t - orders; begin length = 1
+ t[0] = size;
+ int s = 0;
+ for (int i=0; t[i]>=33; i++)
+ {
+ // extension array t
+ int[] tmp_t = new int[t.Length + 1]; ///////////////
+ Array.Copy(t,0,tmp_t,0,t.Length); // extension
+ t = new int[tmp_t.Length]; // array t
+ Array.Copy(tmp_t, 0, t, 0, tmp_t.Length); ///////////////
+
+ t[i+1] = t[i]/2;
+ s = i+1;
+ }
+
+ //step3
+ BigInteger[] p = new BigInteger[s+1];
+ p[s] = new BigInteger("8000000B",16); //set min prime number length 32 bit
+
+ int m = s-1; //step4
+
+ for (int i=0; i<s; i++)
+ {
+ int rm = t[m]/32; //step5
+
+ step6: for(;;)
+ {
+ //step 6
+ BigInteger[] tmp_y = new BigInteger[y.Length]; ////////////////
+ Array.Copy(y,0,tmp_y,0,y.Length); // extension
+ y = new BigInteger[rm+1]; // array y
+ Array.Copy(tmp_y,0,y,0,tmp_y.Length); ////////////////
+
+ for (int j=0; j<rm; j++)
+ {
+ y[j+1] = (y[j].Multiply(constA32).Add(C)).Mod(BigInteger.Two.Pow(32));
+ }
+
+ //step 7
+ BigInteger Ym = BigInteger.Zero;
+ for (int j=0; j<rm; j++)
+ {
+ Ym = Ym.Add(y[j].ShiftLeft(32*j));
+ }
+
+ y[0] = y[rm]; //step 8
+
+ //step 9
+ BigInteger N = BigInteger.One.ShiftLeft(t[m]-1).Divide(p[m+1]).Add(
+ Ym.ShiftLeft(t[m]-1).Divide(p[m+1].ShiftLeft(32*rm)));
+
+ if (N.TestBit(0))
+ {
+ N = N.Add(BigInteger.One);
+ }
+
+ //step 10
+
+ for(;;)
+ {
+ //step 11
+ BigInteger NByLastP = N.Multiply(p[m+1]);
+
+ if (NByLastP.BitLength > t[m])
+ {
+ goto step6; //step 12
+ }
+
+ p[m] = NByLastP.Add(BigInteger.One);
+
+ //step13
+ if (BigInteger.Two.ModPow(NByLastP, p[m]).CompareTo(BigInteger.One) == 0
+ && BigInteger.Two.ModPow(N, p[m]).CompareTo(BigInteger.One) != 0)
+ {
+ break;
+ }
+
+ N = N.Add(BigInteger.Two);
+ }
+
+ if (--m < 0)
+ {
+ pq[0] = p[0];
+ pq[1] = p[1];
+ return y[0].LongValue; //return for procedure B' step 2
+ }
+
+ break; //step 14
+ }
+ }
+ return y[0].LongValue;
+ }
+
+ //Procedure B
+ private void procedure_B(int x0, int c, BigInteger[] pq)
+ {
+ //Verify and perform condition: 0<x<2^16; 0<c<2^16; c - odd.
+ while(x0<0 || x0>65536)
+ {
+ x0 = init_random.NextInt()/32768;
+ }
+
+ while((c<0 || c>65536) || (c/2==0))
+ {
+ c = init_random.NextInt()/32768 + 1;
+ }
+
+ BigInteger [] qp = new BigInteger[2];
+ BigInteger q = null, Q = null, p = null;
+ BigInteger C = BigInteger.ValueOf(c);
+ BigInteger constA16 = BigInteger.ValueOf(19381);
+
+ //step1
+ x0 = procedure_A(x0, c, qp, 256);
+ q = qp[0];
+
+ //step2
+ x0 = procedure_A(x0, c, qp, 512);
+ Q = qp[0];
+
+ BigInteger[] y = new BigInteger[65];
+ y[0] = BigInteger.ValueOf(x0);
+
+ const int tp = 1024;
+
+ BigInteger qQ = q.Multiply(Q);
+
+step3:
+ for(;;)
+ {
+ //step 3
+ for (int j=0; j<64; j++)
+ {
+ y[j+1] = (y[j].Multiply(constA16).Add(C)).Mod(BigInteger.Two.Pow(16));
+ }
+
+ //step 4
+ BigInteger Y = BigInteger.Zero;
+
+ for (int j=0; j<64; j++)
+ {
+ Y = Y.Add(y[j].ShiftLeft(16*j));
+ }
+
+ y[0] = y[64]; //step 5
+
+ //step 6
+ BigInteger N = BigInteger.One.ShiftLeft(tp-1).Divide(qQ).Add(
+ Y.ShiftLeft(tp-1).Divide(qQ.ShiftLeft(1024)));
+
+ if (N.TestBit(0))
+ {
+ N = N.Add(BigInteger.One);
+ }
+
+ //step 7
+
+ for(;;)
+ {
+ //step 11
+ BigInteger qQN = qQ.Multiply(N);
+
+ if (qQN.BitLength > tp)
+ {
+ goto step3; //step 9
+ }
+
+ p = qQN.Add(BigInteger.One);
+
+ //step10
+ if (BigInteger.Two.ModPow(qQN, p).CompareTo(BigInteger.One) == 0
+ && BigInteger.Two.ModPow(q.Multiply(N), p).CompareTo(BigInteger.One) != 0)
+ {
+ pq[0] = p;
+ pq[1] = q;
+ return;
+ }
+
+ N = N.Add(BigInteger.Two);
+ }
+ }
+ }
+
+ //Procedure B'
+ private void procedure_Bb(long x0, long c, BigInteger[] pq)
+ {
+ //Verify and perform condition: 0<x<2^32; 0<c<2^32; c - odd.
+ while(x0<0 || x0>4294967296L)
+ {
+ x0 = init_random.NextInt()*2;
+ }
+
+ while((c<0 || c>4294967296L) || (c/2==0))
+ {
+ c = init_random.NextInt()*2+1;
+ }
+
+ BigInteger [] qp = new BigInteger[2];
+ BigInteger q = null, Q = null, p = null;
+ BigInteger C = BigInteger.ValueOf(c);
+ BigInteger constA32 = BigInteger.ValueOf(97781173);
+
+ //step1
+ x0 = procedure_Aa(x0, c, qp, 256);
+ q = qp[0];
+
+ //step2
+ x0 = procedure_Aa(x0, c, qp, 512);
+ Q = qp[0];
+
+ BigInteger[] y = new BigInteger[33];
+ y[0] = BigInteger.ValueOf(x0);
+
+ const int tp = 1024;
+
+ BigInteger qQ = q.Multiply(Q);
+
+step3:
+ for(;;)
+ {
+ //step 3
+ for (int j=0; j<32; j++)
+ {
+ y[j+1] = (y[j].Multiply(constA32).Add(C)).Mod(BigInteger.Two.Pow(32));
+ }
+
+ //step 4
+ BigInteger Y = BigInteger.Zero;
+ for (int j=0; j<32; j++)
+ {
+ Y = Y.Add(y[j].ShiftLeft(32*j));
+ }
+
+ y[0] = y[32]; //step 5
+
+ //step 6
+ BigInteger N = BigInteger.One.ShiftLeft(tp-1).Divide(qQ).Add(
+ Y.ShiftLeft(tp-1).Divide(qQ.ShiftLeft(1024)));
+
+ if (N.TestBit(0))
+ {
+ N = N.Add(BigInteger.One);
+ }
+
+ //step 7
+
+ for(;;)
+ {
+ //step 11
+ BigInteger qQN = qQ.Multiply(N);
+
+ if (qQN.BitLength > tp)
+ {
+ goto step3; //step 9
+ }
+
+ p = qQN.Add(BigInteger.One);
+
+ //step10
+ if (BigInteger.Two.ModPow(qQN, p).CompareTo(BigInteger.One) == 0
+ && BigInteger.Two.ModPow(q.Multiply(N), p).CompareTo(BigInteger.One) != 0)
+ {
+ pq[0] = p;
+ pq[1] = q;
+ return;
+ }
+
+ N = N.Add(BigInteger.Two);
+ }
+ }
+ }
+
+
+ /**
+ * Procedure C
+ * procedure generates the a value from the given p,q,
+ * returning the a value.
+ */
+ private BigInteger procedure_C(BigInteger p, BigInteger q)
+ {
+ BigInteger pSub1 = p.Subtract(BigInteger.One);
+ BigInteger pSub1Divq = pSub1.Divide(q);
+
+ for(;;)
+ {
+ BigInteger d = new BigInteger(p.BitLength, init_random);
+
+ // 1 < d < p-1
+ if (d.CompareTo(BigInteger.One) > 0 && d.CompareTo(pSub1) < 0)
+ {
+ BigInteger a = d.ModPow(pSub1Divq, p);
+
+ if (a.CompareTo(BigInteger.One) != 0)
+ {
+ return a;
+ }
+ }
+ }
+ }
+
+ /**
+ * which generates the p , q and a values from the given parameters,
+ * returning the Gost3410Parameters object.
+ */
+ public Gost3410Parameters GenerateParameters()
+ {
+ BigInteger [] pq = new BigInteger[2];
+ BigInteger q = null, p = null, a = null;
+
+ int x0, c;
+ long x0L, cL;
+
+ if (typeproc==1)
+ {
+ x0 = init_random.NextInt();
+ c = init_random.NextInt();
+
+ switch(size)
+ {
+ case 512:
+ procedure_A(x0, c, pq, 512);
+ break;
+ case 1024:
+ procedure_B(x0, c, pq);
+ break;
+ default:
+ throw new ArgumentException("Ooops! key size 512 or 1024 bit.");
+ }
+ p = pq[0]; q = pq[1];
+ a = procedure_C(p, q);
+ //System.out.println("p:"+p.toString(16)+"\n"+"q:"+q.toString(16)+"\n"+"a:"+a.toString(16));
+ //System.out.println("p:"+p+"\n"+"q:"+q+"\n"+"a:"+a);
+ return new Gost3410Parameters(p, q, a, new Gost3410ValidationParameters(x0, c));
+ }
+ else
+ {
+ x0L = init_random.NextLong();
+ cL = init_random.NextLong();
+
+ switch(size)
+ {
+ case 512:
+ procedure_Aa(x0L, cL, pq, 512);
+ break;
+ case 1024:
+ procedure_Bb(x0L, cL, pq);
+ break;
+ default:
+ throw new InvalidOperationException("Ooops! key size 512 or 1024 bit.");
+ }
+ p = pq[0]; q = pq[1];
+ a = procedure_C(p, q);
+ //System.out.println("p:"+p.toString(16)+"\n"+"q:"+q.toString(16)+"\n"+"a:"+a.toString(16));
+ //System.out.println("p:"+p+"\n"+"q:"+q+"\n"+"a:"+a);
+ return new Gost3410Parameters(p, q, a, new Gost3410ValidationParameters(x0L, cL));
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/Kdf1BytesGenerator.cs b/Crypto/src/crypto/generators/Kdf1BytesGenerator.cs
new file mode 100644
index 000000000..2b4fb7efd
--- /dev/null
+++ b/Crypto/src/crypto/generators/Kdf1BytesGenerator.cs
@@ -0,0 +1,27 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * KFD2 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
+ * <br/>
+ * This implementation is based on IEEE P1363/ISO 18033.
+ */
+ public class Kdf1BytesGenerator
+ : BaseKdfBytesGenerator
+ {
+ /**
+ * Construct a KDF1 byte generator.
+ *
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ public Kdf1BytesGenerator(
+ IDigest digest)
+ : base(0, digest)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/Kdf2BytesGenerator.cs b/Crypto/src/crypto/generators/Kdf2BytesGenerator.cs
new file mode 100644
index 000000000..be1cd158e
--- /dev/null
+++ b/Crypto/src/crypto/generators/Kdf2BytesGenerator.cs
@@ -0,0 +1,28 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * KDF2 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
+ * <br/>
+ * This implementation is based on IEEE P1363/ISO 18033.
+ */
+ public class Kdf2BytesGenerator
+ : BaseKdfBytesGenerator
+ {
+ /**
+ * Construct a KDF2 bytes generator. Generates key material
+ * according to IEEE P1363 or ISO 18033 depending on the initialisation.
+ *
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ public Kdf2BytesGenerator(
+ IDigest digest)
+ : base(1, digest)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/Mgf1BytesGenerator.cs b/Crypto/src/crypto/generators/Mgf1BytesGenerator.cs
new file mode 100644
index 000000000..23a3aca25
--- /dev/null
+++ b/Crypto/src/crypto/generators/Mgf1BytesGenerator.cs
@@ -0,0 +1,117 @@
+using System;
+//using Org.BouncyCastle.Math;
+//using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * Generator for MGF1 as defined in Pkcs 1v2
+ */
+ public class Mgf1BytesGenerator : IDerivationFunction
+ {
+ private IDigest digest;
+ private byte[] seed;
+ private int hLen;
+
+ /**
+ * @param digest the digest to be used as the source of Generated bytes
+ */
+ public Mgf1BytesGenerator(
+ IDigest digest)
+ {
+ this.digest = digest;
+ this.hLen = digest.GetDigestSize();
+ }
+
+ public void Init(
+ IDerivationParameters parameters)
+ {
+ if (!(typeof(MgfParameters).IsInstanceOfType(parameters)))
+ {
+ throw new ArgumentException("MGF parameters required for MGF1Generator");
+ }
+
+ MgfParameters p = (MgfParameters)parameters;
+
+ seed = p.GetSeed();
+ }
+
+ /**
+ * return the underlying digest.
+ */
+ public IDigest Digest
+ {
+ get
+ {
+ return digest;
+ }
+ }
+
+ /**
+ * int to octet string.
+ */
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)((uint) i >> 24);
+ sp[1] = (byte)((uint) i >> 16);
+ sp[2] = (byte)((uint) i >> 8);
+ sp[3] = (byte)((uint) i >> 0);
+ }
+
+ /**
+ * fill len bytes of the output buffer with bytes Generated from
+ * the derivation function.
+ *
+ * @throws DataLengthException if the out buffer is too small.
+ */
+ public int GenerateBytes(
+ byte[] output,
+ int outOff,
+ int length)
+ {
+ if ((output.Length - length) < outOff)
+ {
+ throw new DataLengthException("output buffer too small");
+ }
+
+ byte[] hashBuf = new byte[hLen];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ digest.Reset();
+
+ if (length > hLen)
+ {
+ do
+ {
+ ItoOSP(counter, C);
+
+ digest.BlockUpdate(seed, 0, seed.Length);
+ digest.BlockUpdate(C, 0, C.Length);
+ digest.DoFinal(hashBuf, 0);
+
+ Array.Copy(hashBuf, 0, output, outOff + counter * hLen, hLen);
+ }
+ while (++counter < (length / hLen));
+ }
+
+ if ((counter * hLen) < length)
+ {
+ ItoOSP(counter, C);
+
+ digest.BlockUpdate(seed, 0, seed.Length);
+ digest.BlockUpdate(C, 0, C.Length);
+ digest.DoFinal(hashBuf, 0);
+
+ Array.Copy(hashBuf, 0, output, outOff + counter * hLen, length - (counter * hLen));
+ }
+
+ return length;
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs b/Crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs
new file mode 100644
index 000000000..a00a6c8a6
--- /dev/null
+++ b/Crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs
@@ -0,0 +1,333 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * Key generation parameters for NaccacheStern cipher. For details on this cipher, please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+ public class NaccacheSternKeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+ {
+ private static readonly int[] smallPrimes =
+ {
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
+ 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
+ 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
+ 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331,
+ 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
+ 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523,
+ 541, 547, 557
+ };
+
+ private NaccacheSternKeyGenerationParameters param;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#init(org.bouncycastle.crypto.KeyGenerationParameters)
+ */
+ public void Init(KeyGenerationParameters parameters)
+ {
+ this.param = (NaccacheSternKeyGenerationParameters)parameters;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#generateKeyPair()
+ */
+ public AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ int strength = param.Strength;
+ SecureRandom rand = param.Random;
+ int certainty = param.Certainty;
+ bool debug = param.IsDebug;
+
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("Fetching first " + param.CountSmallPrimes + " primes.");
+ }
+
+ IList smallPrimes = findFirstPrimes(param.CountSmallPrimes);
+
+ smallPrimes = permuteList(smallPrimes, rand);
+
+ BigInteger u = BigInteger.One;
+ BigInteger v = BigInteger.One;
+
+ for (int i = 0; i < smallPrimes.Count / 2; i++)
+ {
+ u = u.Multiply((BigInteger)smallPrimes[i]);
+ }
+ for (int i = smallPrimes.Count / 2; i < smallPrimes.Count; i++)
+ {
+ v = v.Multiply((BigInteger)smallPrimes[i]);
+ }
+
+ BigInteger sigma = u.Multiply(v);
+
+ // n = (2 a u _p + 1 ) ( 2 b v _q + 1)
+ // -> |n| = strength
+ // |2| = 1 in bits
+ // -> |a| * |b| = |n| - |u| - |v| - |_p| - |_q| - |2| -|2|
+ // remainingStrength = strength - sigma.bitLength() - _p.bitLength() -
+ // _q.bitLength() - 1 -1
+ int remainingStrength = strength - sigma.BitLength - 48;
+ BigInteger a = generatePrime(remainingStrength / 2 + 1, certainty, rand);
+ BigInteger b = generatePrime(remainingStrength / 2 + 1, certainty, rand);
+
+ BigInteger _p;
+ BigInteger _q;
+ BigInteger p;
+ BigInteger q;
+
+ long tries = 0;
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("generating p and q");
+ }
+
+ BigInteger _2au = a.Multiply(u).ShiftLeft(1);
+ BigInteger _2bv = b.Multiply(v).ShiftLeft(1);
+
+ for (;;)
+ {
+ tries++;
+
+ _p = generatePrime(24, certainty, rand);
+
+ p = _p.Multiply(_2au).Add(BigInteger.One);
+
+ if (!p.IsProbablePrime(certainty))
+ continue;
+
+ for (;;)
+ {
+ _q = generatePrime(24, certainty, rand);
+
+ if (_p.Equals(_q))
+ continue;
+
+ q = _q.Multiply(_2bv).Add(BigInteger.One);
+
+ if (q.IsProbablePrime(certainty))
+ break;
+ }
+
+ if (!sigma.Gcd(_p.Multiply(_q)).Equals(BigInteger.One))
+ {
+ System.Diagnostics.Debug.WriteLine("sigma.gcd(_p.mult(_q)) != 1!\n _p: " + _p + "\n _q: " + _q);
+ continue;
+ }
+
+ if (p.Multiply(q).BitLength < strength)
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("key size too small. Should be " + strength + " but is actually "
+ + p.Multiply(q).BitLength);
+ }
+ continue;
+ }
+ break;
+ }
+
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("needed " + tries + " tries to generate p and q.");
+ }
+
+ BigInteger n = p.Multiply(q);
+ BigInteger phi_n = p.Subtract(BigInteger.One).Multiply(q.Subtract(BigInteger.One));
+ BigInteger g;
+ tries = 0;
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("generating g");
+ }
+ for (;;)
+ {
+ // TODO After the first loop, just regenerate one randomly-selected gPart each time?
+ IList gParts = Platform.CreateArrayList();
+ for (int ind = 0; ind != smallPrimes.Count; ind++)
+ {
+ BigInteger i = (BigInteger)smallPrimes[ind];
+ BigInteger e = phi_n.Divide(i);
+
+ for (;;)
+ {
+ tries++;
+
+ g = generatePrime(strength, certainty, rand);
+
+ if (!g.ModPow(e, n).Equals(BigInteger.One))
+ {
+ gParts.Add(g);
+ break;
+ }
+ }
+ }
+ g = BigInteger.One;
+ for (int i = 0; i < smallPrimes.Count; i++)
+ {
+ BigInteger gPart = (BigInteger) gParts[i];
+ BigInteger smallPrime = (BigInteger) smallPrimes[i];
+ g = g.Multiply(gPart.ModPow(sigma.Divide(smallPrime), n)).Mod(n);
+ }
+
+ // make sure that g is not divisible by p_i or q_i
+ bool divisible = false;
+ for (int i = 0; i < smallPrimes.Count; i++)
+ {
+ if (g.ModPow(phi_n.Divide((BigInteger)smallPrimes[i]), n).Equals(BigInteger.One))
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("g has order phi(n)/" + smallPrimes[i] + "\n g: " + g);
+ }
+ divisible = true;
+ break;
+ }
+ }
+
+ if (divisible)
+ {
+ continue;
+ }
+
+ // make sure that g has order > phi_n/4
+
+ //if (g.ModPow(phi_n.Divide(BigInteger.ValueOf(4)), n).Equals(BigInteger.One))
+ if (g.ModPow(phi_n.ShiftRight(2), n).Equals(BigInteger.One))
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("g has order phi(n)/4\n g:" + g);
+ }
+ continue;
+ }
+
+ if (g.ModPow(phi_n.Divide(_p), n).Equals(BigInteger.One))
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("g has order phi(n)/p'\n g: " + g);
+ }
+ continue;
+ }
+ if (g.ModPow(phi_n.Divide(_q), n).Equals(BigInteger.One))
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("g has order phi(n)/q'\n g: " + g);
+ }
+ continue;
+ }
+ if (g.ModPow(phi_n.Divide(a), n).Equals(BigInteger.One))
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("g has order phi(n)/a\n g: " + g);
+ }
+ continue;
+ }
+ if (g.ModPow(phi_n.Divide(b), n).Equals(BigInteger.One))
+ {
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("g has order phi(n)/b\n g: " + g);
+ }
+ continue;
+ }
+ break;
+ }
+ if (debug)
+ {
+ System.Diagnostics.Debug.WriteLine("needed " + tries + " tries to generate g");
+ System.Diagnostics.Debug.WriteLine("");
+ System.Diagnostics.Debug.WriteLine("found new NaccacheStern cipher variables:");
+ System.Diagnostics.Debug.WriteLine("smallPrimes: " + CollectionUtilities.ToString(smallPrimes));
+ System.Diagnostics.Debug.WriteLine("sigma:...... " + sigma + " (" + sigma.BitLength + " bits)");
+ System.Diagnostics.Debug.WriteLine("a:.......... " + a);
+ System.Diagnostics.Debug.WriteLine("b:.......... " + b);
+ System.Diagnostics.Debug.WriteLine("p':......... " + _p);
+ System.Diagnostics.Debug.WriteLine("q':......... " + _q);
+ System.Diagnostics.Debug.WriteLine("p:.......... " + p);
+ System.Diagnostics.Debug.WriteLine("q:.......... " + q);
+ System.Diagnostics.Debug.WriteLine("n:.......... " + n);
+ System.Diagnostics.Debug.WriteLine("phi(n):..... " + phi_n);
+ System.Diagnostics.Debug.WriteLine("g:.......... " + g);
+ System.Diagnostics.Debug.WriteLine("");
+ }
+
+ return new AsymmetricCipherKeyPair(new NaccacheSternKeyParameters(false, g, n, sigma.BitLength),
+ new NaccacheSternPrivateKeyParameters(g, n, sigma.BitLength, smallPrimes, phi_n));
+ }
+
+ private static BigInteger generatePrime(
+ int bitLength,
+ int certainty,
+ SecureRandom rand)
+ {
+ return new BigInteger(bitLength, certainty, rand);
+ }
+
+ /**
+ * Generates a permuted ArrayList from the original one. The original List
+ * is not modified
+ *
+ * @param arr
+ * the ArrayList to be permuted
+ * @param rand
+ * the source of Randomness for permutation
+ * @return a new ArrayList with the permuted elements.
+ */
+ private static IList permuteList(
+ IList arr,
+ SecureRandom rand)
+ {
+ // TODO Create a utility method for generating permutation of first 'n' integers
+
+ IList retval = Platform.CreateArrayList(arr.Count);
+
+ foreach (object element in arr)
+ {
+ int index = rand.Next(retval.Count + 1);
+ retval.Insert(index, element);
+ }
+
+ return retval;
+ }
+
+ /**
+ * Finds the first 'count' primes starting with 3
+ *
+ * @param count
+ * the number of primes to find
+ * @return a vector containing the found primes as Integer
+ */
+ private static IList findFirstPrimes(
+ int count)
+ {
+ IList primes = Platform.CreateArrayList(count);
+
+ for (int i = 0; i != count; i++)
+ {
+ primes.Add(BigInteger.ValueOf(smallPrimes[i]));
+ }
+
+ return primes;
+ }
+
+ }
+}
diff --git a/Crypto/src/crypto/generators/OpenSSLPBEParametersGenerator.cs b/Crypto/src/crypto/generators/OpenSSLPBEParametersGenerator.cs
new file mode 100644
index 000000000..8da5d3ad1
--- /dev/null
+++ b/Crypto/src/crypto/generators/OpenSSLPBEParametersGenerator.cs
@@ -0,0 +1,167 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * Generator for PBE derived keys and ivs as usd by OpenSSL.
+ * <p>
+ * The scheme is a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an
+ * iteration count of 1.
+ * </p>
+ */
+ public class OpenSslPbeParametersGenerator
+ : PbeParametersGenerator
+ {
+ private readonly IDigest digest = new MD5Digest();
+
+ /**
+ * Construct a OpenSSL Parameters generator.
+ */
+ public OpenSslPbeParametersGenerator()
+ {
+ }
+
+ public override void Init(
+ byte[] password,
+ byte[] salt,
+ int iterationCount)
+ {
+ // Ignore the provided iterationCount
+ base.Init(password, salt, 1);
+ }
+
+ /**
+ * Initialise - note the iteration count for this algorithm is fixed at 1.
+ *
+ * @param password password to use.
+ * @param salt salt to use.
+ */
+ public virtual void Init(
+ byte[] password,
+ byte[] salt)
+ {
+ base.Init(password, salt, 1);
+ }
+
+ /**
+ * the derived key function, the ith hash of the password and the salt.
+ */
+ private byte[] GenerateDerivedKey(
+ int bytesNeeded)
+ {
+ byte[] buf = new byte[digest.GetDigestSize()];
+ byte[] key = new byte[bytesNeeded];
+ int offset = 0;
+
+ for (;;)
+ {
+ digest.BlockUpdate(mPassword, 0, mPassword.Length);
+ digest.BlockUpdate(mSalt, 0, mSalt.Length);
+
+ digest.DoFinal(buf, 0);
+
+ int len = (bytesNeeded > buf.Length) ? buf.Length : bytesNeeded;
+ Array.Copy(buf, 0, key, offset, len);
+ offset += len;
+
+ // check if we need any more
+ bytesNeeded -= len;
+ if (bytesNeeded == 0)
+ {
+ break;
+ }
+
+ // do another round
+ digest.Reset();
+ digest.BlockUpdate(buf, 0, buf.Length);
+ }
+
+ return key;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception ArgumentException if the key length larger than the base hash size.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public override ICipherParameters GenerateDerivedParameters(
+ int keySize)
+ {
+ return GenerateDerivedMacParameters(keySize);
+ }
+
+ public override ICipherParameters GenerateDerivedParameters(
+ string algorithm,
+ int keySize)
+ {
+ keySize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(keySize);
+
+ return ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ * @exception ArgumentException if keySize + ivSize is larger than the base hash size.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public override ICipherParameters GenerateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize = keySize / 8;
+ ivSize = ivSize / 8;
+
+ byte[] dKey = GenerateDerivedKey(keySize + ivSize);
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
+ }
+
+ public override ICipherParameters GenerateDerivedParameters(
+ string algorithm,
+ int keySize,
+ int ivSize)
+ {
+ keySize /= 8;
+ ivSize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(keySize + ivSize);
+ KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize);
+
+ return new ParametersWithIV(key, dKey, keySize, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception ArgumentException if the key length larger than the base hash size.
+ */
+ public override ICipherParameters GenerateDerivedMacParameters(
+ int keySize)
+ {
+ keySize = keySize / 8;
+
+ byte[] dKey = GenerateDerivedKey(keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs b/Crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs
new file mode 100644
index 000000000..d2da3f6fc
--- /dev/null
+++ b/Crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs
@@ -0,0 +1,245 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * Generator for Pbe derived keys and ivs as defined by Pkcs 12 V1.0.
+ * <p>
+ * The document this implementation is based on can be found at
+ * <a href="http://www.rsasecurity.com/rsalabs/pkcs/pkcs-12/index.html">
+ * RSA's Pkcs12 Page</a>
+ * </p>
+ */
+ public class Pkcs12ParametersGenerator
+ : PbeParametersGenerator
+ {
+ public const int KeyMaterial = 1;
+ public const int IVMaterial = 2;
+ public const int MacMaterial = 3;
+
+ private readonly IDigest digest;
+
+ private readonly int u;
+ private readonly int v;
+
+ /**
+ * Construct a Pkcs 12 Parameters generator.
+ *
+ * @param digest the digest to be used as the source of derived keys.
+ * @exception ArgumentException if an unknown digest is passed in.
+ */
+ public Pkcs12ParametersGenerator(
+ IDigest digest)
+ {
+ this.digest = digest;
+
+ u = digest.GetDigestSize();
+ v = digest.GetByteLength();
+ }
+
+ /**
+ * add a + b + 1, returning the result in a. The a value is treated
+ * as a BigInteger of length (b.Length * 8) bits. The result is
+ * modulo 2^b.Length in case of overflow.
+ */
+ private void Adjust(
+ byte[] a,
+ int aOff,
+ byte[] b)
+ {
+ int x = (b[b.Length - 1] & 0xff) + (a[aOff + b.Length - 1] & 0xff) + 1;
+
+ a[aOff + b.Length - 1] = (byte)x;
+ x = (int) ((uint) x >> 8);
+
+ for (int i = b.Length - 2; i >= 0; i--)
+ {
+ x += (b[i] & 0xff) + (a[aOff + i] & 0xff);
+ a[aOff + i] = (byte)x;
+ x = (int) ((uint) x >> 8);
+ }
+ }
+
+ /**
+ * generation of a derived key ala Pkcs12 V1.0.
+ */
+ private byte[] GenerateDerivedKey(
+ int idByte,
+ int n)
+ {
+ byte[] D = new byte[v];
+ byte[] dKey = new byte[n];
+
+ for (int i = 0; i != D.Length; i++)
+ {
+ D[i] = (byte)idByte;
+ }
+
+ byte[] S;
+
+ if ((mSalt != null) && (mSalt.Length != 0))
+ {
+ S = new byte[v * ((mSalt.Length + v - 1) / v)];
+
+ for (int i = 0; i != S.Length; i++)
+ {
+ S[i] = mSalt[i % mSalt.Length];
+ }
+ }
+ else
+ {
+ S = new byte[0];
+ }
+
+ byte[] P;
+
+ if ((mPassword != null) && (mPassword.Length != 0))
+ {
+ P = new byte[v * ((mPassword.Length + v - 1) / v)];
+
+ for (int i = 0; i != P.Length; i++)
+ {
+ P[i] = mPassword[i % mPassword.Length];
+ }
+ }
+ else
+ {
+ P = new byte[0];
+ }
+
+ byte[] I = new byte[S.Length + P.Length];
+
+ Array.Copy(S, 0, I, 0, S.Length);
+ Array.Copy(P, 0, I, S.Length, P.Length);
+
+ byte[] B = new byte[v];
+ int c = (n + u - 1) / u;
+
+ for (int i = 1; i <= c; i++)
+ {
+ byte[] A = new byte[u];
+
+ digest.BlockUpdate(D, 0, D.Length);
+ digest.BlockUpdate(I, 0, I.Length);
+ digest.DoFinal(A, 0);
+ for (int j = 1; j != mIterationCount; j++)
+ {
+ digest.BlockUpdate(A, 0, A.Length);
+ digest.DoFinal(A, 0);
+ }
+
+ for (int j = 0; j != B.Length; j++)
+ {
+ B[j] = A[j % A.Length];
+ }
+
+ for (int j = 0; j != I.Length / v; j++)
+ {
+ Adjust(I, j * v, B);
+ }
+
+ if (i == c)
+ {
+ Array.Copy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u));
+ }
+ else
+ {
+ Array.Copy(A, 0, dKey, (i - 1) * u, A.Length);
+ }
+ }
+
+ return dKey;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public override ICipherParameters GenerateDerivedParameters(
+ int keySize)
+ {
+ keySize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+
+ public override ICipherParameters GenerateDerivedParameters(
+ string algorithm,
+ int keySize)
+ {
+ keySize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize);
+
+ return ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public override ICipherParameters GenerateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize /= 8;
+ ivSize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize);
+
+ byte[] iv = GenerateDerivedKey(IVMaterial, ivSize);
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize);
+ }
+
+ public override ICipherParameters GenerateDerivedParameters(
+ string algorithm,
+ int keySize,
+ int ivSize)
+ {
+ keySize /= 8;
+ ivSize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize);
+ KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize);
+
+ byte[] iv = GenerateDerivedKey(IVMaterial, ivSize);
+
+ return new ParametersWithIV(key, iv, 0, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public override ICipherParameters GenerateDerivedMacParameters(
+ int keySize)
+ {
+ keySize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(MacMaterial, keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/Pkcs5S1ParametersGenerator.cs b/Crypto/src/crypto/generators/Pkcs5S1ParametersGenerator.cs
new file mode 100644
index 000000000..8586e1ca9
--- /dev/null
+++ b/Crypto/src/crypto/generators/Pkcs5S1ParametersGenerator.cs
@@ -0,0 +1,162 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * Generator for Pbe derived keys and ivs as defined by Pkcs 5 V2.0 Scheme 1.
+ * Note this generator is limited to the size of the hash produced by the
+ * digest used to drive it.
+ * <p>
+ * The document this implementation is based on can be found at
+ * <a href="http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html">
+ * RSA's Pkcs5 Page</a>
+ * </p>
+ */
+ public class Pkcs5S1ParametersGenerator
+ : PbeParametersGenerator
+ {
+ private readonly IDigest digest;
+
+ /**
+ * Construct a Pkcs 5 Scheme 1 Parameters generator.
+ *
+ * @param digest the digest to be used as the source of derived keys.
+ */
+ public Pkcs5S1ParametersGenerator(
+ IDigest digest)
+ {
+ this.digest = digest;
+ }
+
+ /**
+ * the derived key function, the ith hash of the mPassword and the mSalt.
+ */
+ private byte[] GenerateDerivedKey()
+ {
+ byte[] digestBytes = new byte[digest.GetDigestSize()];
+
+ digest.BlockUpdate(mPassword, 0, mPassword.Length);
+ digest.BlockUpdate(mSalt, 0, mSalt.Length);
+
+ digest.DoFinal(digestBytes, 0);
+ for (int i = 1; i < mIterationCount; i++)
+ {
+ digest.BlockUpdate(digestBytes, 0, digestBytes.Length);
+ digest.DoFinal(digestBytes, 0);
+ }
+
+ return digestBytes;
+ }
+
+ /**
+ * Generate a key parameter derived from the mPassword, mSalt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception ArgumentException if the key length larger than the base hash size.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public override ICipherParameters GenerateDerivedParameters(
+ int keySize)
+ {
+ return GenerateDerivedMacParameters(keySize);
+ }
+
+ public override ICipherParameters GenerateDerivedParameters(
+ string algorithm,
+ int keySize)
+ {
+ keySize /= 8;
+
+ if (keySize > digest.GetDigestSize())
+ {
+ throw new ArgumentException(
+ "Can't Generate a derived key " + keySize + " bytes long.");
+ }
+
+ byte[] dKey = GenerateDerivedKey();
+
+ return ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the mPassword, mSalt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ * @exception ArgumentException if keySize + ivSize is larger than the base hash size.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public override ICipherParameters GenerateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize /= 8;
+ ivSize /= 8;
+
+ if ((keySize + ivSize) > digest.GetDigestSize())
+ {
+ throw new ArgumentException(
+ "Can't Generate a derived key " + (keySize + ivSize) + " bytes long.");
+ }
+
+ byte[] dKey = GenerateDerivedKey();
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
+ }
+
+ public override ICipherParameters GenerateDerivedParameters(
+ string algorithm,
+ int keySize,
+ int ivSize)
+ {
+ keySize /= 8;
+ ivSize /= 8;
+
+ if ((keySize + ivSize) > digest.GetDigestSize())
+ {
+ throw new ArgumentException(
+ "Can't Generate a derived key " + (keySize + ivSize) + " bytes long.");
+ }
+
+ byte[] dKey = GenerateDerivedKey();
+ KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize);
+
+ return new ParametersWithIV(key, dKey, keySize, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the mPassword,
+ * mSalt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ * @exception ArgumentException if the key length larger than the base hash size.
+ */
+ public override ICipherParameters GenerateDerivedMacParameters(
+ int keySize)
+ {
+ keySize /= 8;
+
+ if (keySize > digest.GetDigestSize())
+ {
+ throw new ArgumentException(
+ "Can't Generate a derived key " + keySize + " bytes long.");
+ }
+
+ byte[] dKey = GenerateDerivedKey();
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs b/Crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs
new file mode 100644
index 000000000..58d7b5c37
--- /dev/null
+++ b/Crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs
@@ -0,0 +1,172 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * Generator for Pbe derived keys and ivs as defined by Pkcs 5 V2.0 Scheme 2.
+ * This generator uses a SHA-1 HMac as the calculation function.
+ * <p>
+ * The document this implementation is based on can be found at
+ * <a href="http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html">
+ * RSA's Pkcs5 Page</a></p>
+ */
+ public class Pkcs5S2ParametersGenerator
+ : PbeParametersGenerator
+ {
+ private readonly IMac hMac;
+
+ /**
+ * construct a Pkcs5 Scheme 2 Parameters generator.
+ */
+ public Pkcs5S2ParametersGenerator()
+ : this(new Sha1Digest())
+ {
+ }
+
+ public Pkcs5S2ParametersGenerator(IDigest digest)
+ {
+ hMac = new HMac(digest);
+ }
+
+ private void F(
+ byte[] P,
+ byte[] S,
+ int c,
+ byte[] iBuf,
+ byte[] outBytes,
+ int outOff)
+ {
+ byte[] state = new byte[hMac.GetMacSize()];
+ ICipherParameters param = new KeyParameter(P);
+
+ hMac.Init(param);
+
+ if (S != null)
+ {
+ hMac.BlockUpdate(S, 0, S.Length);
+ }
+
+ hMac.BlockUpdate(iBuf, 0, iBuf.Length);
+
+ hMac.DoFinal(state, 0);
+
+ Array.Copy(state, 0, outBytes, outOff, state.Length);
+
+ for (int count = 1; count != c; count++)
+ {
+ hMac.Init(param);
+ hMac.BlockUpdate(state, 0, state.Length);
+ hMac.DoFinal(state, 0);
+
+ for (int j = 0; j != state.Length; j++)
+ {
+ outBytes[outOff + j] ^= state[j];
+ }
+ }
+ }
+
+ private byte[] GenerateDerivedKey(
+ int dkLen)
+ {
+ int hLen = hMac.GetMacSize();
+ int l = (dkLen + hLen - 1) / hLen;
+ byte[] iBuf = new byte[4];
+ byte[] outBytes = new byte[l * hLen];
+
+ for (int i = 1; i <= l; i++)
+ {
+ Pack.UInt32_To_BE((uint)i, iBuf);
+
+ F(mPassword, mSalt, mIterationCount, iBuf, outBytes, (i - 1) * hLen);
+ }
+
+ return outBytes;
+ }
+
+ /**
+ * Generate a key parameter derived from the password, salt, and iteration
+ * count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public override ICipherParameters GenerateDerivedParameters(
+ int keySize)
+ {
+ return GenerateDerivedMacParameters(keySize);
+ }
+
+ public override ICipherParameters GenerateDerivedParameters(
+ string algorithm,
+ int keySize)
+ {
+ keySize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(keySize);
+
+ return ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize);
+ }
+
+ /**
+ * Generate a key with initialisation vector parameter derived from
+ * the password, salt, and iteration count we are currently initialised
+ * with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @param ivSize the size of the iv we want (in bits)
+ * @return a ParametersWithIV object.
+ */
+ [Obsolete("Use version with 'algorithm' parameter")]
+ public override ICipherParameters GenerateDerivedParameters(
+ int keySize,
+ int ivSize)
+ {
+ keySize /= 8;
+ ivSize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(keySize + ivSize);
+
+ return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
+ }
+
+ public override ICipherParameters GenerateDerivedParameters(
+ string algorithm,
+ int keySize,
+ int ivSize)
+ {
+ keySize /= 8;
+ ivSize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(keySize + ivSize);
+ KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize);
+
+ return new ParametersWithIV(key, dKey, keySize, ivSize);
+ }
+
+ /**
+ * Generate a key parameter for use with a MAC derived from the password,
+ * salt, and iteration count we are currently initialised with.
+ *
+ * @param keySize the size of the key we want (in bits)
+ * @return a KeyParameter object.
+ */
+ public override ICipherParameters GenerateDerivedMacParameters(
+ int keySize)
+ {
+ keySize /= 8;
+
+ byte[] dKey = GenerateDerivedKey(keySize);
+
+ return new KeyParameter(dKey, 0, keySize);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/RSABlindingFactorGenerator.cs b/Crypto/src/crypto/generators/RSABlindingFactorGenerator.cs
new file mode 100644
index 000000000..e2f63face
--- /dev/null
+++ b/Crypto/src/crypto/generators/RSABlindingFactorGenerator.cs
@@ -0,0 +1,69 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * Generate a random factor suitable for use with RSA blind signatures
+ * as outlined in Chaum's blinding and unblinding as outlined in
+ * "Handbook of Applied Cryptography", page 475.
+ */
+ public class RsaBlindingFactorGenerator
+ {
+ private RsaKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * Initialise the factor generator
+ *
+ * @param param the necessary RSA key parameters.
+ */
+ public void Init(
+ ICipherParameters param)
+ {
+ if (param is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (RsaKeyParameters)rParam.Parameters;
+ random = rParam.Random;
+ }
+ else
+ {
+ key = (RsaKeyParameters)param;
+ random = new SecureRandom();
+ }
+
+ if (key.IsPrivate)
+ throw new ArgumentException("generator requires RSA public key");
+ }
+
+ /**
+ * Generate a suitable blind factor for the public key the generator was initialised with.
+ *
+ * @return a random blind factor
+ */
+ public BigInteger GenerateBlindingFactor()
+ {
+ if (key == null)
+ throw new InvalidOperationException("generator not initialised");
+
+ BigInteger m = key.Modulus;
+ int length = m.BitLength - 1; // must be less than m.BitLength
+ BigInteger factor;
+ BigInteger gcd;
+
+ do
+ {
+ factor = new BigInteger(length, random);
+ gcd = factor.Gcd(m);
+ }
+ while (factor.SignValue == 0 || factor.Equals(BigInteger.One) || !gcd.Equals(BigInteger.One));
+
+ return factor;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/generators/RsaKeyPairGenerator.cs b/Crypto/src/crypto/generators/RsaKeyPairGenerator.cs
new file mode 100644
index 000000000..3074aed04
--- /dev/null
+++ b/Crypto/src/crypto/generators/RsaKeyPairGenerator.cs
@@ -0,0 +1,139 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /**
+ * an RSA key pair generator.
+ */
+ public class RsaKeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+ {
+ private static readonly BigInteger DefaultPublicExponent = BigInteger.ValueOf(0x10001);
+ private const int DefaultTests = 12;
+
+ private RsaKeyGenerationParameters param;
+
+ public void Init(
+ KeyGenerationParameters parameters)
+ {
+ if (parameters is RsaKeyGenerationParameters)
+ {
+ this.param = (RsaKeyGenerationParameters)parameters;
+ }
+ else
+ {
+ this.param = new RsaKeyGenerationParameters(
+ DefaultPublicExponent, parameters.Random, parameters.Strength, DefaultTests);
+ }
+ }
+
+ public AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ BigInteger p, q, n, d, e, pSub1, qSub1, phi;
+
+ //
+ // p and q values should have a length of half the strength in bits
+ //
+ int strength = param.Strength;
+ int pbitlength = (strength + 1) / 2;
+ int qbitlength = (strength - pbitlength);
+ int mindiffbits = strength / 3;
+
+ e = param.PublicExponent;
+
+ // TODO Consider generating safe primes for p, q (see DHParametersHelper.generateSafePrimes)
+ // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm")
+
+ //
+ // Generate p, prime and (p-1) relatively prime to e
+ //
+ for (;;)
+ {
+ p = new BigInteger(pbitlength, 1, param.Random);
+
+ if (p.Mod(e).Equals(BigInteger.One))
+ continue;
+
+ if (!p.IsProbablePrime(param.Certainty))
+ continue;
+
+ if (e.Gcd(p.Subtract(BigInteger.One)).Equals(BigInteger.One))
+ break;
+ }
+
+ //
+ // Generate a modulus of the required length
+ //
+ for (;;)
+ {
+ // Generate q, prime and (q-1) relatively prime to e,
+ // and not equal to p
+ //
+ for (;;)
+ {
+ q = new BigInteger(qbitlength, 1, param.Random);
+
+ if (q.Subtract(p).Abs().BitLength < mindiffbits)
+ continue;
+
+ if (q.Mod(e).Equals(BigInteger.One))
+ continue;
+
+ if (!q.IsProbablePrime(param.Certainty))
+ continue;
+
+ if (e.Gcd(q.Subtract(BigInteger.One)).Equals(BigInteger.One))
+ break;
+ }
+
+ //
+ // calculate the modulus
+ //
+ n = p.Multiply(q);
+
+ if (n.BitLength == param.Strength)
+ break;
+
+ //
+ // if we Get here our primes aren't big enough, make the largest
+ // of the two p and try again
+ //
+ p = p.Max(q);
+ }
+
+ if (p.CompareTo(q) < 0)
+ {
+ phi = p;
+ p = q;
+ q = phi;
+ }
+
+ pSub1 = p.Subtract(BigInteger.One);
+ qSub1 = q.Subtract(BigInteger.One);
+ phi = pSub1.Multiply(qSub1);
+
+ //
+ // calculate the private exponent
+ //
+ d = e.ModInverse(phi);
+
+ //
+ // calculate the CRT factors
+ //
+ BigInteger dP, dQ, qInv;
+
+ dP = d.Remainder(pSub1);
+ dQ = d.Remainder(qSub1);
+ qInv = q.ModInverse(p);
+
+ return new AsymmetricCipherKeyPair(
+ new RsaKeyParameters(false, n, e),
+ new RsaPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/generators/SCrypt.cs b/Crypto/src/crypto/generators/SCrypt.cs
new file mode 100644
index 000000000..efa74d735
--- /dev/null
+++ b/Crypto/src/crypto/generators/SCrypt.cs
@@ -0,0 +1,140 @@
+using System;
+using System.Threading;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ public class SCrypt
+ {
+ // TODO Validate arguments
+ public static byte[] Generate(byte[] P, byte[] S, int N, int r, int p, int dkLen)
+ {
+ return MFcrypt(P, S, N, r, p, dkLen);
+ }
+
+ private static byte[] MFcrypt(byte[] P, byte[] S, int N, int r, int p, int dkLen)
+ {
+ int MFLenBytes = r * 128;
+ byte[] bytes = SingleIterationPBKDF2(P, S, p * MFLenBytes);
+
+ uint[] B = null;
+
+ try
+ {
+ int BLen = bytes.Length >> 2;
+ B = new uint[BLen];
+
+ Pack.LE_To_UInt32(bytes, 0, B);
+
+ int MFLenWords = MFLenBytes >> 2;
+ for (int BOff = 0; BOff < BLen; BOff += MFLenWords)
+ {
+ // TODO These can be done in parallel threads
+ SMix(B, BOff, N, r);
+ }
+
+ Pack.UInt32_To_LE(B, bytes, 0);
+
+ return SingleIterationPBKDF2(P, bytes, dkLen);
+ }
+ finally
+ {
+ ClearAll(bytes, B);
+ }
+ }
+
+ private static byte[] SingleIterationPBKDF2(byte[] P, byte[] S, int dkLen)
+ {
+ PbeParametersGenerator pGen = new Pkcs5S2ParametersGenerator(new Sha256Digest());
+ pGen.Init(P, S, 1);
+ KeyParameter key = (KeyParameter)pGen.GenerateDerivedMacParameters(dkLen * 8);
+ return key.GetKey();
+ }
+
+ private static void SMix(uint[] B, int BOff, int N, int r)
+ {
+ int BCount = r * 32;
+
+ uint[] blockX1 = new uint[16];
+ uint[] blockX2 = new uint[16];
+ uint[] blockY = new uint[BCount];
+
+ uint[] X = new uint[BCount];
+ uint[][] V = new uint[N][];
+
+ try
+ {
+ Array.Copy(B, BOff, X, 0, BCount);
+
+ for (int i = 0; i < N; ++i)
+ {
+ V[i] = (uint[])X.Clone();
+ BlockMix(X, blockX1, blockX2, blockY, r);
+ }
+
+ uint mask = (uint)N - 1;
+ for (int i = 0; i < N; ++i)
+ {
+ uint j = X[BCount - 16] & mask;
+ Xor(X, V[j], 0, X);
+ BlockMix(X, blockX1, blockX2, blockY, r);
+ }
+
+ Array.Copy(X, 0, B, BOff, BCount);
+ }
+ finally
+ {
+ ClearAll(V);
+ ClearAll(X, blockX1, blockX2, blockY);
+ }
+ }
+
+ private static void BlockMix(uint[] B, uint[] X1, uint[] X2, uint[] Y, int r)
+ {
+ Array.Copy(B, B.Length - 16, X1, 0, 16);
+
+ int BOff = 0, YOff = 0, halfLen = B.Length >> 1;
+
+ for (int i = 2 * r; i > 0; --i)
+ {
+ Xor(X1, B, BOff, X2);
+
+ Salsa20Engine.SalsaCore(8, X2, X1);
+ Array.Copy(X1, 0, Y, YOff, 16);
+
+ YOff = halfLen + BOff - YOff;
+ BOff += 16;
+ }
+
+ Array.Copy(Y, 0, B, 0, Y.Length);
+ }
+
+ private static void Xor(uint[] a, uint[] b, int bOff, uint[] output)
+ {
+ for (int i = output.Length - 1; i >= 0; --i)
+ {
+ output[i] = a[i] ^ b[bOff + i];
+ }
+ }
+
+ private static void Clear(Array array)
+ {
+ if (array != null)
+ {
+ Array.Clear(array, 0, array.Length);
+ }
+ }
+
+ private static void ClearAll(params Array[] arrays)
+ {
+ foreach (Array array in arrays)
+ {
+ Clear(array);
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/io/CipherStream.cs b/Crypto/src/crypto/io/CipherStream.cs
new file mode 100644
index 000000000..bf7effb0a
--- /dev/null
+++ b/Crypto/src/crypto/io/CipherStream.cs
@@ -0,0 +1,237 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.IO
+{
+ public class CipherStream
+ : Stream
+ {
+ internal Stream stream;
+ internal IBufferedCipher inCipher, outCipher;
+ private byte[] mInBuf;
+ private int mInPos;
+ private bool inStreamEnded;
+
+ public CipherStream(
+ Stream stream,
+ IBufferedCipher readCipher,
+ IBufferedCipher writeCipher)
+ {
+ this.stream = stream;
+
+ if (readCipher != null)
+ {
+ this.inCipher = readCipher;
+ mInBuf = null;
+ }
+
+ if (writeCipher != null)
+ {
+ this.outCipher = writeCipher;
+ }
+ }
+
+ public IBufferedCipher ReadCipher
+ {
+ get { return inCipher; }
+ }
+
+ public IBufferedCipher WriteCipher
+ {
+ get { return outCipher; }
+ }
+
+ public override int ReadByte()
+ {
+ if (inCipher == null)
+ return stream.ReadByte();
+
+ if (mInBuf == null || mInPos >= mInBuf.Length)
+ {
+ if (!FillInBuf())
+ return -1;
+ }
+
+ return mInBuf[mInPos++];
+ }
+
+ public override int Read(
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ if (inCipher == null)
+ return stream.Read(buffer, offset, count);
+
+ int num = 0;
+ while (num < count)
+ {
+ if (mInBuf == null || mInPos >= mInBuf.Length)
+ {
+ if (!FillInBuf())
+ break;
+ }
+
+ int numToCopy = System.Math.Min(count - num, mInBuf.Length - mInPos);
+ Array.Copy(mInBuf, mInPos, buffer, offset + num, numToCopy);
+ mInPos += numToCopy;
+ num += numToCopy;
+ }
+
+ return num;
+ }
+
+ private bool FillInBuf()
+ {
+ if (inStreamEnded)
+ return false;
+
+ mInPos = 0;
+
+ do
+ {
+ mInBuf = ReadAndProcessBlock();
+ }
+ while (!inStreamEnded && mInBuf == null);
+
+ return mInBuf != null;
+ }
+
+ private byte[] ReadAndProcessBlock()
+ {
+ int blockSize = inCipher.GetBlockSize();
+ int readSize = (blockSize == 0) ? 256 : blockSize;
+
+ byte[] block = new byte[readSize];
+ int numRead = 0;
+ do
+ {
+ int count = stream.Read(block, numRead, block.Length - numRead);
+ if (count < 1)
+ {
+ inStreamEnded = true;
+ break;
+ }
+ numRead += count;
+ }
+ while (numRead < block.Length);
+
+ Debug.Assert(inStreamEnded || numRead == block.Length);
+
+ byte[] bytes = inStreamEnded
+ ? inCipher.DoFinal(block, 0, numRead)
+ : inCipher.ProcessBytes(block);
+
+ if (bytes != null && bytes.Length == 0)
+ {
+ bytes = null;
+ }
+
+ return bytes;
+ }
+
+ public override void Write(
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ Debug.Assert(buffer != null);
+ Debug.Assert(0 <= offset && offset <= buffer.Length);
+ Debug.Assert(count >= 0);
+
+ int end = offset + count;
+
+ Debug.Assert(0 <= end && end <= buffer.Length);
+
+ if (outCipher == null)
+ {
+ stream.Write(buffer, offset, count);
+ return;
+ }
+
+ byte[] data = outCipher.ProcessBytes(buffer, offset, count);
+ if (data != null)
+ {
+ stream.Write(data, 0, data.Length);
+ }
+ }
+
+ public override void WriteByte(
+ byte b)
+ {
+ if (outCipher == null)
+ {
+ stream.WriteByte(b);
+ return;
+ }
+
+ byte[] data = outCipher.ProcessByte(b);
+ if (data != null)
+ {
+ stream.Write(data, 0, data.Length);
+ }
+ }
+
+ public override bool CanRead
+ {
+ get { return stream.CanRead && (inCipher != null); }
+ }
+
+ public override bool CanWrite
+ {
+ get { return stream.CanWrite && (outCipher != null); }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public sealed override long Length
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public sealed override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (outCipher != null)
+ {
+ byte[] data = outCipher.DoFinal();
+ stream.Write(data, 0, data.Length);
+ stream.Flush();
+ }
+ stream.Dispose();
+ }
+ }
+
+ public override void Flush()
+ {
+ // Note: outCipher.DoFinal is only called during Close()
+ stream.Flush();
+ }
+
+ public sealed override long Seek(
+ long offset,
+ SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public sealed override void SetLength(
+ long length)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/io/DigestStream.cs b/Crypto/src/crypto/io/DigestStream.cs
new file mode 100644
index 000000000..a5b31f95c
--- /dev/null
+++ b/Crypto/src/crypto/io/DigestStream.cs
@@ -0,0 +1,140 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.IO
+{
+ public class DigestStream
+ : Stream
+ {
+ protected readonly Stream stream;
+ protected readonly IDigest inDigest;
+ protected readonly IDigest outDigest;
+
+ public DigestStream(
+ Stream stream,
+ IDigest readDigest,
+ IDigest writeDigest)
+ {
+ this.stream = stream;
+ this.inDigest = readDigest;
+ this.outDigest = writeDigest;
+ }
+
+ public virtual IDigest ReadDigest()
+ {
+ return inDigest;
+ }
+
+ public virtual IDigest WriteDigest()
+ {
+ return outDigest;
+ }
+
+ public override int Read(
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ int n = stream.Read(buffer, offset, count);
+ if (inDigest != null)
+ {
+ if (n > 0)
+ {
+ inDigest.BlockUpdate(buffer, offset, n);
+ }
+ }
+ return n;
+ }
+
+ public override int ReadByte()
+ {
+ int b = stream.ReadByte();
+ if (inDigest != null)
+ {
+ if (b >= 0)
+ {
+ inDigest.Update((byte)b);
+ }
+ }
+ return b;
+ }
+
+ public override void Write(
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ if (outDigest != null)
+ {
+ if (count > 0)
+ {
+ outDigest.BlockUpdate(buffer, offset, count);
+ }
+ }
+ stream.Write(buffer, offset, count);
+ }
+
+ public override void WriteByte(
+ byte b)
+ {
+ if (outDigest != null)
+ {
+ outDigest.Update(b);
+ }
+ stream.WriteByte(b);
+ }
+
+ public override bool CanRead
+ {
+ get { return stream.CanRead; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return stream.CanWrite; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return stream.CanSeek; }
+ }
+
+ public override long Length
+ {
+ get { return stream.Length; }
+ }
+
+ public override long Position
+ {
+ get { return stream.Position; }
+ set { stream.Position = value; }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ stream.Dispose();
+ }
+ }
+
+ public override void Flush()
+ {
+ stream.Flush();
+ }
+
+ public override long Seek(
+ long offset,
+ SeekOrigin origin)
+ {
+ return stream.Seek(offset, origin);
+ }
+
+ public override void SetLength(
+ long length)
+ {
+ stream.SetLength(length);
+ }
+ }
+}
+
diff --git a/Crypto/src/crypto/io/MacStream.cs b/Crypto/src/crypto/io/MacStream.cs
new file mode 100644
index 000000000..419eafb77
--- /dev/null
+++ b/Crypto/src/crypto/io/MacStream.cs
@@ -0,0 +1,139 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.IO
+{
+ public class MacStream
+ : Stream
+ {
+ protected readonly Stream stream;
+ protected readonly IMac inMac;
+ protected readonly IMac outMac;
+
+ public MacStream(
+ Stream stream,
+ IMac readMac,
+ IMac writeMac)
+ {
+ this.stream = stream;
+ this.inMac = readMac;
+ this.outMac = writeMac;
+ }
+
+ public virtual IMac ReadMac()
+ {
+ return inMac;
+ }
+
+ public virtual IMac WriteMac()
+ {
+ return outMac;
+ }
+
+ public override int Read(
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ int n = stream.Read(buffer, offset, count);
+ if (inMac != null)
+ {
+ if (n > 0)
+ {
+ inMac.BlockUpdate(buffer, offset, n);
+ }
+ }
+ return n;
+ }
+
+ public override int ReadByte()
+ {
+ int b = stream.ReadByte();
+ if (inMac != null)
+ {
+ if (b >= 0)
+ {
+ inMac.Update((byte)b);
+ }
+ }
+ return b;
+ }
+
+ public override void Write(
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ if (outMac != null)
+ {
+ if (count > 0)
+ {
+ outMac.BlockUpdate(buffer, offset, count);
+ }
+ }
+ stream.Write(buffer, offset, count);
+ }
+
+ public override void WriteByte(byte b)
+ {
+ if (outMac != null)
+ {
+ outMac.Update(b);
+ }
+ stream.WriteByte(b);
+ }
+
+ public override bool CanRead
+ {
+ get { return stream.CanRead; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return stream.CanWrite; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return stream.CanSeek; }
+ }
+
+ public override long Length
+ {
+ get { return stream.Length; }
+ }
+
+ public override long Position
+ {
+ get { return stream.Position; }
+ set { stream.Position = value; }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ stream.Dispose();
+ }
+ }
+
+ public override void Flush()
+ {
+ stream.Flush();
+ }
+
+ public override long Seek(
+ long offset,
+ SeekOrigin origin)
+ {
+ return stream.Seek(offset,origin);
+ }
+
+ public override void SetLength(
+ long length)
+ {
+ stream.SetLength(length);
+ }
+ }
+}
+
diff --git a/Crypto/src/crypto/io/SignerStream.cs b/Crypto/src/crypto/io/SignerStream.cs
new file mode 100644
index 000000000..8be8ca84a
--- /dev/null
+++ b/Crypto/src/crypto/io/SignerStream.cs
@@ -0,0 +1,140 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.IO
+{
+ public class SignerStream
+ : Stream
+ {
+ protected readonly Stream stream;
+ protected readonly ISigner inSigner;
+ protected readonly ISigner outSigner;
+
+ public SignerStream(
+ Stream stream,
+ ISigner readSigner,
+ ISigner writeSigner)
+ {
+ this.stream = stream;
+ this.inSigner = readSigner;
+ this.outSigner = writeSigner;
+ }
+
+ public virtual ISigner ReadSigner()
+ {
+ return inSigner;
+ }
+
+ public virtual ISigner WriteSigner()
+ {
+ return outSigner;
+ }
+
+ public override int Read(
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ int n = stream.Read(buffer, offset, count);
+ if (inSigner != null)
+ {
+ if (n > 0)
+ {
+ inSigner.BlockUpdate(buffer, offset, n);
+ }
+ }
+ return n;
+ }
+
+ public override int ReadByte()
+ {
+ int b = stream.ReadByte();
+ if (inSigner != null)
+ {
+ if (b >= 0)
+ {
+ inSigner.Update((byte)b);
+ }
+ }
+ return b;
+ }
+
+ public override void Write(
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ if (outSigner != null)
+ {
+ if (count > 0)
+ {
+ outSigner.BlockUpdate(buffer, offset, count);
+ }
+ }
+ stream.Write(buffer, offset, count);
+ }
+
+ public override void WriteByte(
+ byte b)
+ {
+ if (outSigner != null)
+ {
+ outSigner.Update(b);
+ }
+ stream.WriteByte(b);
+ }
+
+ public override bool CanRead
+ {
+ get { return stream.CanRead; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return stream.CanWrite; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return stream.CanSeek; }
+ }
+
+ public override long Length
+ {
+ get { return stream.Length; }
+ }
+
+ public override long Position
+ {
+ get { return stream.Position; }
+ set { stream.Position = value; }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ stream.Dispose();
+ }
+ }
+
+ public override void Flush()
+ {
+ stream.Flush();
+ }
+
+ public override long Seek(
+ long offset,
+ SeekOrigin origin)
+ {
+ return stream.Seek(offset, origin);
+ }
+
+ public override void SetLength(
+ long length)
+ {
+ stream.SetLength(length);
+ }
+ }
+}
+
diff --git a/Crypto/src/crypto/macs/CMac.cs b/Crypto/src/crypto/macs/CMac.cs
new file mode 100644
index 000000000..ea1ce88f5
--- /dev/null
+++ b/Crypto/src/crypto/macs/CMac.cs
@@ -0,0 +1,240 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Paddings;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+ /**
+ * CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
+ * <p>
+ * CMAC is analogous to OMAC1 - see also en.wikipedia.org/wiki/CMAC
+ * </p><p>
+ * CMAC is a NIST recomendation - see
+ * csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
+ * </p><p>
+ * CMAC/OMAC1 is a blockcipher-based message authentication code designed and
+ * analyzed by Tetsu Iwata and Kaoru Kurosawa.
+ * </p><p>
+ * CMAC/OMAC1 is a simple variant of the CBC MAC (Cipher Block Chaining Message
+ * Authentication Code). OMAC stands for One-Key CBC MAC.
+ * </p><p>
+ * It supports 128- or 64-bits block ciphers, with any key size, and returns
+ * a MAC with dimension less or equal to the block size of the underlying
+ * cipher.
+ * </p>
+ */
+ public class CMac
+ : IMac
+ {
+ private const byte CONSTANT_128 = (byte)0x87;
+ private const byte CONSTANT_64 = (byte)0x1b;
+
+ private byte[] ZEROES;
+
+ private byte[] mac;
+
+ private byte[] buf;
+ private int bufOff;
+ private IBlockCipher cipher;
+
+ private int macSize;
+
+ private byte[] L, Lu, Lu2;
+
+ /**
+ * create a standard MAC based on a CBC block cipher (64 or 128 bit block).
+ * This will produce an authentication code the length of the block size
+ * of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ */
+ public CMac(
+ IBlockCipher cipher)
+ : this(cipher, cipher.GetBlockSize() * 8)
+ {
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits.
+ * <p/>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8 and @lt;= 128.
+ */
+ public CMac(
+ IBlockCipher cipher,
+ int macSizeInBits)
+ {
+ if ((macSizeInBits % 8) != 0)
+ throw new ArgumentException("MAC size must be multiple of 8");
+
+ if (macSizeInBits > (cipher.GetBlockSize() * 8))
+ {
+ throw new ArgumentException(
+ "MAC size must be less or equal to "
+ + (cipher.GetBlockSize() * 8));
+ }
+
+ if (cipher.GetBlockSize() != 8 && cipher.GetBlockSize() != 16)
+ {
+ throw new ArgumentException(
+ "Block size must be either 64 or 128 bits");
+ }
+
+ this.cipher = new CbcBlockCipher(cipher);
+ this.macSize = macSizeInBits / 8;
+
+ mac = new byte[cipher.GetBlockSize()];
+
+ buf = new byte[cipher.GetBlockSize()];
+
+ ZEROES = new byte[cipher.GetBlockSize()];
+
+ bufOff = 0;
+ }
+
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName; }
+ }
+
+ private byte[] doubleLu(
+ byte[] inBytes)
+ {
+ int FirstBit = (inBytes[0] & 0xFF) >> 7;
+ byte[] ret = new byte[inBytes.Length];
+ for (int i = 0; i < inBytes.Length - 1; i++)
+ {
+ ret[i] = (byte)((inBytes[i] << 1) + ((inBytes[i + 1] & 0xFF) >> 7));
+ }
+ ret[inBytes.Length - 1] = (byte)(inBytes[inBytes.Length - 1] << 1);
+ if (FirstBit == 1)
+ {
+ ret[inBytes.Length - 1] ^= inBytes.Length == 16 ? CONSTANT_128 : CONSTANT_64;
+ }
+ return ret;
+ }
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ Reset();
+
+ cipher.Init(true, parameters);
+
+ //initializes the L, Lu, Lu2 numbers
+ L = new byte[ZEROES.Length];
+ cipher.ProcessBlock(ZEROES, 0, L, 0);
+ Lu = doubleLu(L);
+ Lu2 = doubleLu(Lu);
+
+ cipher.Init(true, parameters);
+ }
+
+ public int GetMacSize()
+ {
+ return macSize;
+ }
+
+ public void Update(
+ byte input)
+ {
+ if (bufOff == buf.Length)
+ {
+ cipher.ProcessBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = input;
+ }
+
+ public void BlockUpdate(
+ byte[] inBytes,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ throw new ArgumentException("Can't have a negative input length!");
+
+ int blockSize = cipher.GetBlockSize();
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ Array.Copy(inBytes, inOff, buf, bufOff, gapLen);
+
+ cipher.ProcessBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ cipher.ProcessBlock(inBytes, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ Array.Copy(inBytes, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int DoFinal(
+ byte[] outBytes,
+ int outOff)
+ {
+ int blockSize = cipher.GetBlockSize();
+
+ byte[] lu;
+ if (bufOff == blockSize)
+ {
+ lu = Lu;
+ }
+ else
+ {
+ new ISO7816d4Padding().AddPadding(buf, bufOff);
+ lu = Lu2;
+ }
+
+ for (int i = 0; i < mac.Length; i++)
+ {
+ buf[i] ^= lu[i];
+ }
+
+ cipher.ProcessBlock(buf, 0, mac, 0);
+
+ Array.Copy(mac, 0, outBytes, outOff, macSize);
+
+ Reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void Reset()
+ {
+ /*
+ * clean the buffer.
+ */
+ Array.Clear(buf, 0, buf.Length);
+ bufOff = 0;
+
+ /*
+ * Reset the underlying cipher.
+ */
+ cipher.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/macs/CbcBlockCipherMac.cs b/Crypto/src/crypto/macs/CbcBlockCipherMac.cs
new file mode 100644
index 000000000..146e16aa8
--- /dev/null
+++ b/Crypto/src/crypto/macs/CbcBlockCipherMac.cs
@@ -0,0 +1,209 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Paddings;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+ /**
+ * standard CBC Block Cipher MAC - if no padding is specified the default of
+ * pad of zeroes is used.
+ */
+ public class CbcBlockCipherMac
+ : IMac
+ {
+ private byte[] buf;
+ private int bufOff;
+ private IBlockCipher cipher;
+ private IBlockCipherPadding padding;
+ private int macSize;
+
+ /**
+ * create a standard MAC based on a CBC block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ */
+ public CbcBlockCipherMac(
+ IBlockCipher cipher)
+ : this(cipher, (cipher.GetBlockSize() * 8) / 2, null)
+ {
+ }
+
+ /**
+ * create a standard MAC based on a CBC block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public CbcBlockCipherMac(
+ IBlockCipher cipher,
+ IBlockCipherPadding padding)
+ : this(cipher, (cipher.GetBlockSize() * 8) / 2, padding)
+ {
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CBC mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ * </p>
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ */
+ public CbcBlockCipherMac(
+ IBlockCipher cipher,
+ int macSizeInBits)
+ : this(cipher, macSizeInBits, null)
+ {
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CBC mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ * </p>
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public CbcBlockCipherMac(
+ IBlockCipher cipher,
+ int macSizeInBits,
+ IBlockCipherPadding padding)
+ {
+ if ((macSizeInBits % 8) != 0)
+ throw new ArgumentException("MAC size must be multiple of 8");
+
+ this.cipher = new CbcBlockCipher(cipher);
+ this.padding = padding;
+ this.macSize = macSizeInBits / 8;
+
+ buf = new byte[cipher.GetBlockSize()];
+ bufOff = 0;
+ }
+
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName; }
+ }
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ Reset();
+
+ cipher.Init(true, parameters);
+ }
+
+ public int GetMacSize()
+ {
+ return macSize;
+ }
+
+ public void Update(
+ byte input)
+ {
+ if (bufOff == buf.Length)
+ {
+ cipher.ProcessBlock(buf, 0, buf, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = input;
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ throw new ArgumentException("Can't have a negative input length!");
+
+ int blockSize = cipher.GetBlockSize();
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ Array.Copy(input, inOff, buf, bufOff, gapLen);
+
+ cipher.ProcessBlock(buf, 0, buf, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ cipher.ProcessBlock(input, inOff, buf, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ Array.Copy(input, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ int blockSize = cipher.GetBlockSize();
+
+ if (padding == null)
+ {
+ // pad with zeroes
+ while (bufOff < blockSize)
+ {
+ buf[bufOff++] = 0;
+ }
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ cipher.ProcessBlock(buf, 0, buf, 0);
+ bufOff = 0;
+ }
+
+ padding.AddPadding(buf, bufOff);
+ }
+
+ cipher.ProcessBlock(buf, 0, buf, 0);
+
+ Array.Copy(buf, 0, output, outOff, macSize);
+
+ Reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void Reset()
+ {
+ // Clear the buffer.
+ Array.Clear(buf, 0, buf.Length);
+ bufOff = 0;
+
+ // Reset the underlying cipher.
+ cipher.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/macs/CfbBlockCipherMac.cs b/Crypto/src/crypto/macs/CfbBlockCipherMac.cs
new file mode 100644
index 000000000..364cf8499
--- /dev/null
+++ b/Crypto/src/crypto/macs/CfbBlockCipherMac.cs
@@ -0,0 +1,368 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Paddings;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+ /**
+ * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
+ */
+ class MacCFBBlockCipher
+ : IBlockCipher
+ {
+ private byte[] IV;
+ private byte[] cfbV;
+ private byte[] cfbOutV;
+
+ private readonly int blockSize;
+ private readonly IBlockCipher cipher;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param blockSize the block size in bits (note: a multiple of 8)
+ */
+ public MacCFBBlockCipher(
+ IBlockCipher cipher,
+ int bitBlockSize)
+ {
+ this.cipher = cipher;
+ this.blockSize = bitBlockSize / 8;
+
+ this.IV = new byte[cipher.GetBlockSize()];
+ this.cfbV = new byte[cipher.GetBlockSize()];
+ this.cfbOutV = new byte[cipher.GetBlockSize()];
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param param the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)parameters;
+ byte[] iv = ivParam.GetIV();
+
+ if (iv.Length < IV.Length)
+ {
+ Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
+ }
+ else
+ {
+ Array.Copy(iv, 0, IV, 0, IV.Length);
+ }
+
+ parameters = ivParam.Parameters;
+ }
+
+ Reset();
+
+ cipher.Init(true, parameters);
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/CFB"
+ * and the block size in bits.
+ */
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return true; }
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int GetBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ if ((inOff + blockSize) > input.Length)
+ throw new DataLengthException("input buffer too short");
+
+ if ((outOff + blockSize) > outBytes.Length)
+ throw new DataLengthException("output buffer too short");
+
+ cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
+
+ //
+ // XOR the cfbV with the plaintext producing the cipher text
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
+ Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void Reset()
+ {
+ IV.CopyTo(cfbV, 0);
+
+ cipher.Reset();
+ }
+
+ public void GetMacBlock(
+ byte[] mac)
+ {
+ cipher.ProcessBlock(cfbV, 0, mac, 0);
+ }
+ }
+
+ public class CfbBlockCipherMac
+ : IMac
+ {
+ private byte[] mac;
+ private byte[] Buffer;
+ private int bufOff;
+ private MacCFBBlockCipher cipher;
+ private IBlockCipherPadding padding;
+ private int macSize;
+
+ /**
+ * create a standard MAC based on a CFB block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher, with
+ * the CFB mode set to 8 bits.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ */
+ public CfbBlockCipherMac(
+ IBlockCipher cipher)
+ : this(cipher, 8, (cipher.GetBlockSize() * 8) / 2, null)
+ {
+ }
+
+ /**
+ * create a standard MAC based on a CFB block cipher. This will produce an
+ * authentication code half the length of the block size of the cipher, with
+ * the CFB mode set to 8 bits.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param padding the padding to be used.
+ */
+ public CfbBlockCipherMac(
+ IBlockCipher cipher,
+ IBlockCipherPadding padding)
+ : this(cipher, 8, (cipher.GetBlockSize() * 8) / 2, padding)
+ {
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CFB mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ * </p>
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param cfbBitSize the size of an output block produced by the CFB mode.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ */
+ public CfbBlockCipherMac(
+ IBlockCipher cipher,
+ int cfbBitSize,
+ int macSizeInBits)
+ : this(cipher, cfbBitSize, macSizeInBits, null)
+ {
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses CFB mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ * </p>
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param cfbBitSize the size of an output block produced by the CFB mode.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @param padding a padding to be used.
+ */
+ public CfbBlockCipherMac(
+ IBlockCipher cipher,
+ int cfbBitSize,
+ int macSizeInBits,
+ IBlockCipherPadding padding)
+ {
+ if ((macSizeInBits % 8) != 0)
+ throw new ArgumentException("MAC size must be multiple of 8");
+
+ mac = new byte[cipher.GetBlockSize()];
+
+ this.cipher = new MacCFBBlockCipher(cipher, cfbBitSize);
+ this.padding = padding;
+ this.macSize = macSizeInBits / 8;
+
+ Buffer = new byte[this.cipher.GetBlockSize()];
+ bufOff = 0;
+ }
+
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName; }
+ }
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ Reset();
+
+ cipher.Init(true, parameters);
+ }
+
+ public int GetMacSize()
+ {
+ return macSize;
+ }
+
+ public void Update(
+ byte input)
+ {
+ if (bufOff == Buffer.Length)
+ {
+ cipher.ProcessBlock(Buffer, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ Buffer[bufOff++] = input;
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ throw new ArgumentException("Can't have a negative input length!");
+
+ int blockSize = cipher.GetBlockSize();
+ int resultLen = 0;
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ Array.Copy(input, inOff, Buffer, bufOff, gapLen);
+
+ resultLen += cipher.ProcessBlock(Buffer, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ resultLen += cipher.ProcessBlock(input, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ Array.Copy(input, inOff, Buffer, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ int blockSize = cipher.GetBlockSize();
+
+ // pad with zeroes
+ if (this.padding == null)
+ {
+ while (bufOff < blockSize)
+ {
+ Buffer[bufOff++] = 0;
+ }
+ }
+ else
+ {
+ padding.AddPadding(Buffer, bufOff);
+ }
+
+ cipher.ProcessBlock(Buffer, 0, mac, 0);
+
+ cipher.GetMacBlock(mac);
+
+ Array.Copy(mac, 0, output, outOff, macSize);
+
+ Reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void Reset()
+ {
+ // Clear the buffer.
+ Array.Clear(Buffer, 0, Buffer.Length);
+ bufOff = 0;
+
+ // Reset the underlying cipher.
+ cipher.Reset();
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/macs/GOST28147Mac.cs b/Crypto/src/crypto/macs/GOST28147Mac.cs
new file mode 100644
index 000000000..9a8f1b730
--- /dev/null
+++ b/Crypto/src/crypto/macs/GOST28147Mac.cs
@@ -0,0 +1,296 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+ /**
+ * implementation of GOST 28147-89 MAC
+ */
+ public class Gost28147Mac : IMac
+ {
+ private const int blockSize = 8;
+ private const int macSize = 4;
+ private int bufOff;
+ private byte[] buf;
+ private byte[] mac;
+ private bool firstStep = true;
+ private int[] workingKey;
+
+ //
+ // This is default S-box - E_A.
+ private byte[] S =
+ {
+ 0x9,0x6,0x3,0x2,0x8,0xB,0x1,0x7,0xA,0x4,0xE,0xF,0xC,0x0,0xD,0x5,
+ 0x3,0x7,0xE,0x9,0x8,0xA,0xF,0x0,0x5,0x2,0x6,0xC,0xB,0x4,0xD,0x1,
+ 0xE,0x4,0x6,0x2,0xB,0x3,0xD,0x8,0xC,0xF,0x5,0xA,0x0,0x7,0x1,0x9,
+ 0xE,0x7,0xA,0xC,0xD,0x1,0x3,0x9,0x0,0x2,0xB,0x4,0xF,0x8,0x5,0x6,
+ 0xB,0x5,0x1,0x9,0x8,0xD,0xF,0x0,0xE,0x4,0x2,0x3,0xC,0x7,0xA,0x6,
+ 0x3,0xA,0xD,0xC,0x1,0x2,0x0,0xB,0x7,0x5,0x9,0x4,0x8,0xF,0xE,0x6,
+ 0x1,0xD,0x2,0x9,0x7,0xA,0x6,0x0,0x8,0xC,0x4,0x5,0xF,0x3,0xB,0xE,
+ 0xB,0xA,0xF,0x5,0x0,0xC,0xE,0x8,0x6,0x2,0x3,0x9,0x1,0x7,0xD,0x4
+ };
+
+ public Gost28147Mac()
+ {
+ mac = new byte[blockSize];
+ buf = new byte[blockSize];
+ bufOff = 0;
+ }
+
+ private static int[] generateWorkingKey(
+ byte[] userKey)
+ {
+ if (userKey.Length != 32)
+ throw new ArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!");
+
+ int[] key = new int[8];
+ for(int i=0; i!=8; i++)
+ {
+ key[i] = bytesToint(userKey,i*4);
+ }
+
+ return key;
+ }
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ Reset();
+ buf = new byte[blockSize];
+ if (parameters is ParametersWithSBox)
+ {
+ ParametersWithSBox param = (ParametersWithSBox)parameters;
+
+ //
+ // Set the S-Box
+ //
+ param.GetSBox().CopyTo(this.S, 0);
+
+ //
+ // set key if there is one
+ //
+ if (param.Parameters != null)
+ {
+ workingKey = generateWorkingKey(((KeyParameter)param.Parameters).GetKey());
+ }
+ }
+ else if (parameters is KeyParameter)
+ {
+ workingKey = generateWorkingKey(((KeyParameter)parameters).GetKey());
+ }
+ else
+ {
+ throw new ArgumentException("invalid parameter passed to Gost28147 init - "
+ + parameters.GetType().Name);
+ }
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Gost28147Mac"; }
+ }
+
+ public int GetMacSize()
+ {
+ return macSize;
+ }
+
+ private int gost28147_mainStep(int n1, int key)
+ {
+ int cm = (key + n1); // CM1
+
+ // S-box replacing
+
+ int om = S[ 0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4);
+ om += S[ 16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4);
+ om += S[ 32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4);
+ om += S[ 48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4);
+ om += S[ 64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4);
+ om += S[ 80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4);
+ om += S[ 96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4);
+ om += S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4);
+
+// return om << 11 | om >>> (32-11); // 11-leftshift
+ int omLeft = om << 11;
+ int omRight = (int)(((uint) om) >> (32 - 11)); // Note: Casts required to get unsigned bit rotation
+
+ return omLeft | omRight;
+ }
+
+ private void gost28147MacFunc(
+ int[] workingKey,
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ int N1, N2, tmp; //tmp -> for saving N1
+ N1 = bytesToint(input, inOff);
+ N2 = bytesToint(input, inOff + 4);
+
+ for (int k = 0; k < 2; k++) // 1-16 steps
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ tmp = N1;
+ N1 = N2 ^ gost28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+
+ intTobytes(N1, output, outOff);
+ intTobytes(N2, output, outOff + 4);
+ }
+
+ //array of bytes to type int
+ private static int bytesToint(
+ byte[] input,
+ int inOff)
+ {
+ return (int)((input[inOff + 3] << 24) & 0xff000000) + ((input[inOff + 2] << 16) & 0xff0000)
+ + ((input[inOff + 1] << 8) & 0xff00) + (input[inOff] & 0xff);
+ }
+
+ //int to array of bytes
+ private static void intTobytes(
+ int num,
+ byte[] output,
+ int outOff)
+ {
+ output[outOff + 3] = (byte)(num >> 24);
+ output[outOff + 2] = (byte)(num >> 16);
+ output[outOff + 1] = (byte)(num >> 8);
+ output[outOff] = (byte)num;
+ }
+
+ private static byte[] CM5func(
+ byte[] buf,
+ int bufOff,
+ byte[] mac)
+ {
+ byte[] sum = new byte[buf.Length - bufOff];
+
+ Array.Copy(buf, bufOff, sum, 0, mac.Length);
+
+ for (int i = 0; i != mac.Length; i++)
+ {
+ sum[i] = (byte)(sum[i] ^ mac[i]);
+ }
+
+ return sum;
+ }
+
+ public void Update(
+ byte input)
+ {
+ if (bufOff == buf.Length)
+ {
+ byte[] sumbuf = new byte[buf.Length];
+ Array.Copy(buf, 0, sumbuf, 0, mac.Length);
+
+ if (firstStep)
+ {
+ firstStep = false;
+ }
+ else
+ {
+ sumbuf = CM5func(buf, 0, mac);
+ }
+
+ gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = input;
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ throw new ArgumentException("Can't have a negative input length!");
+
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ Array.Copy(input, inOff, buf, bufOff, gapLen);
+
+ byte[] sumbuf = new byte[buf.Length];
+ Array.Copy(buf, 0, sumbuf, 0, mac.Length);
+
+ if (firstStep)
+ {
+ firstStep = false;
+ }
+ else
+ {
+ sumbuf = CM5func(buf, 0, mac);
+ }
+
+ gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ sumbuf = CM5func(input, inOff, mac);
+ gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ Array.Copy(input, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ //padding with zero
+ while (bufOff < blockSize)
+ {
+ buf[bufOff++] = 0;
+ }
+
+ byte[] sumbuf = new byte[buf.Length];
+ Array.Copy(buf, 0, sumbuf, 0, mac.Length);
+
+ if (firstStep)
+ {
+ firstStep = false;
+ }
+ else
+ {
+ sumbuf = CM5func(buf, 0, mac);
+ }
+
+ gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+
+ Array.Copy(mac, (mac.Length/2)-macSize, output, outOff, macSize);
+
+ Reset();
+
+ return macSize;
+ }
+
+ public void Reset()
+ {
+ // Clear the buffer.
+ Array.Clear(buf, 0, buf.Length);
+ bufOff = 0;
+
+ firstStep = true;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/macs/HMac.cs b/Crypto/src/crypto/macs/HMac.cs
new file mode 100644
index 000000000..3f9b0cef0
--- /dev/null
+++ b/Crypto/src/crypto/macs/HMac.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+ /**
+ * HMAC implementation based on RFC2104
+ *
+ * H(K XOR opad, H(K XOR ipad, text))
+ */
+ public class HMac
+ : IMac
+ {
+ private const byte IPAD = (byte)0x36;
+ private const byte OPAD = (byte)0x5C;
+
+ private readonly IDigest digest;
+ private readonly int digestSize;
+ private readonly int blockLength;
+
+ private readonly byte[] inputPad;
+ private readonly byte[] outputPad;
+
+ public HMac(
+ IDigest digest)
+ {
+ this.digest = digest;
+ this.digestSize = digest.GetDigestSize();
+ this.blockLength = digest.GetByteLength();
+ this.inputPad = new byte[blockLength];
+ this.outputPad = new byte[blockLength];
+ }
+
+ public string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "/HMAC"; }
+ }
+
+ public IDigest GetUnderlyingDigest()
+ {
+ return digest;
+ }
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ digest.Reset();
+
+ byte[] key = ((KeyParameter)parameters).GetKey();
+ int keyLength = key.Length;
+
+ if (keyLength > blockLength)
+ {
+ digest.BlockUpdate(key, 0, key.Length);
+ digest.DoFinal(inputPad, 0);
+
+ keyLength = digestSize;
+ }
+ else
+ {
+ Array.Copy(key, 0, inputPad, 0, keyLength);
+ }
+
+ Array.Clear(inputPad, keyLength, blockLength - keyLength);
+ Array.Copy(inputPad, 0, outputPad, 0, blockLength);
+
+ xor(inputPad, IPAD);
+ xor(outputPad, OPAD);
+
+ // Initialise the digest
+ digest.BlockUpdate(inputPad, 0, inputPad.Length);
+ }
+
+ public int GetMacSize()
+ {
+ return digestSize;
+ }
+
+ public void Update(
+ byte input)
+ {
+ digest.Update(input);
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int len)
+ {
+ digest.BlockUpdate(input, inOff, len);
+ }
+
+ public int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ byte[] tmp = new byte[digestSize];
+ digest.DoFinal(tmp, 0);
+
+ digest.BlockUpdate(outputPad, 0, outputPad.Length);
+ digest.BlockUpdate(tmp, 0, tmp.Length);
+
+ int len = digest.DoFinal(output, outOff);
+
+ // Initialise the digest
+ digest.BlockUpdate(inputPad, 0, inputPad.Length);
+
+ return len;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void Reset()
+ {
+ // Reset underlying digest
+ digest.Reset();
+
+ // Initialise the digest
+ digest.BlockUpdate(inputPad, 0, inputPad.Length);
+ }
+
+ private static void xor(byte[] a, byte n)
+ {
+ for (int i = 0; i < a.Length; ++i)
+ {
+ a[i] ^= n;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/macs/ISO9797Alg3Mac.cs b/Crypto/src/crypto/macs/ISO9797Alg3Mac.cs
new file mode 100644
index 000000000..6fee619c1
--- /dev/null
+++ b/Crypto/src/crypto/macs/ISO9797Alg3Mac.cs
@@ -0,0 +1,275 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Paddings;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+ /**
+ * DES based CBC Block Cipher MAC according to ISO9797, algorithm 3 (ANSI X9.19 Retail MAC)
+ *
+ * This could as well be derived from CBCBlockCipherMac, but then the property mac in the base
+ * class must be changed to protected
+ */
+ public class ISO9797Alg3Mac : IMac
+ {
+ private byte[] mac;
+ private byte[] buf;
+ private int bufOff;
+ private IBlockCipher cipher;
+ private IBlockCipherPadding padding;
+ private int macSize;
+ private KeyParameter lastKey2;
+ private KeyParameter lastKey3;
+
+ /**
+ * create a Retail-MAC based on a CBC block cipher. This will produce an
+ * authentication code of the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation. This must
+ * be DESEngine.
+ */
+ public ISO9797Alg3Mac(
+ IBlockCipher cipher)
+ : this(cipher, cipher.GetBlockSize() * 8, null)
+ {
+ }
+
+ /**
+ * create a Retail-MAC based on a CBC block cipher. This will produce an
+ * authentication code of the length of the block size of the cipher.
+ *
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public ISO9797Alg3Mac(
+ IBlockCipher cipher,
+ IBlockCipherPadding padding)
+ : this(cipher, cipher.GetBlockSize() * 8, padding)
+ {
+ }
+
+ /**
+ * create a Retail-MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses single DES CBC mode as the basis for the
+ * MAC generation.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ * </p>
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ */
+ public ISO9797Alg3Mac(
+ IBlockCipher cipher,
+ int macSizeInBits)
+ : this(cipher, macSizeInBits, null)
+ {
+ }
+
+ /**
+ * create a standard MAC based on a block cipher with the size of the
+ * MAC been given in bits. This class uses single DES CBC mode as the basis for the
+ * MAC generation. The final block is decrypted and then encrypted using the
+ * middle and right part of the key.
+ * <p>
+ * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+ * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+ * and in general should be less than the size of the block cipher as it reduces
+ * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+ * </p>
+ * @param cipher the cipher to be used as the basis of the MAC generation.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
+ * @param padding the padding to be used to complete the last block.
+ */
+ public ISO9797Alg3Mac(
+ IBlockCipher cipher,
+ int macSizeInBits,
+ IBlockCipherPadding padding)
+ {
+ if ((macSizeInBits % 8) != 0)
+ throw new ArgumentException("MAC size must be multiple of 8");
+
+ if (!(cipher is DesEngine))
+ throw new ArgumentException("cipher must be instance of DesEngine");
+
+ this.cipher = new CbcBlockCipher(cipher);
+ this.padding = padding;
+ this.macSize = macSizeInBits / 8;
+
+ mac = new byte[cipher.GetBlockSize()];
+ buf = new byte[cipher.GetBlockSize()];
+ bufOff = 0;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "ISO9797Alg3"; }
+ }
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ Reset();
+
+ if (!(parameters is KeyParameter || parameters is ParametersWithIV))
+ throw new ArgumentException("parameters must be an instance of KeyParameter or ParametersWithIV");
+
+ // KeyParameter must contain a double or triple length DES key,
+ // however the underlying cipher is a single DES. The middle and
+ // right key are used only in the final step.
+
+ KeyParameter kp;
+ if (parameters is KeyParameter)
+ {
+ kp = (KeyParameter)parameters;
+ }
+ else
+ {
+ kp = (KeyParameter)((ParametersWithIV)parameters).Parameters;
+ }
+
+ KeyParameter key1;
+ byte[] keyvalue = kp.GetKey();
+
+ if (keyvalue.Length == 16)
+ { // Double length DES key
+ key1 = new KeyParameter(keyvalue, 0, 8);
+ this.lastKey2 = new KeyParameter(keyvalue, 8, 8);
+ this.lastKey3 = key1;
+ }
+ else if (keyvalue.Length == 24)
+ { // Triple length DES key
+ key1 = new KeyParameter(keyvalue, 0, 8);
+ this.lastKey2 = new KeyParameter(keyvalue, 8, 8);
+ this.lastKey3 = new KeyParameter(keyvalue, 16, 8);
+ }
+ else
+ {
+ throw new ArgumentException("Key must be either 112 or 168 bit long");
+ }
+
+ if (parameters is ParametersWithIV)
+ {
+ cipher.Init(true, new ParametersWithIV(key1, ((ParametersWithIV)parameters).GetIV()));
+ }
+ else
+ {
+ cipher.Init(true, key1);
+ }
+ }
+
+ public int GetMacSize()
+ {
+ return macSize;
+ }
+
+ public void Update(
+ byte input)
+ {
+ if (bufOff == buf.Length)
+ {
+ cipher.ProcessBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = input;
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int len)
+ {
+ if (len < 0)
+ throw new ArgumentException("Can't have a negative input length!");
+
+ int blockSize = cipher.GetBlockSize();
+ int resultLen = 0;
+ int gapLen = blockSize - bufOff;
+
+ if (len > gapLen)
+ {
+ Array.Copy(input, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.ProcessBlock(buf, 0, mac, 0);
+
+ bufOff = 0;
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ resultLen += cipher.ProcessBlock(input, inOff, mac, 0);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ Array.Copy(input, inOff, buf, bufOff, len);
+
+ bufOff += len;
+ }
+
+ public int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ int blockSize = cipher.GetBlockSize();
+
+ if (padding == null)
+ {
+ // pad with zeroes
+ while (bufOff < blockSize)
+ {
+ buf[bufOff++] = 0;
+ }
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ cipher.ProcessBlock(buf, 0, mac, 0);
+ bufOff = 0;
+ }
+
+ padding.AddPadding(buf, bufOff);
+ }
+
+ cipher.ProcessBlock(buf, 0, mac, 0);
+
+ // Added to code from base class
+ DesEngine deseng = new DesEngine();
+
+ deseng.Init(false, this.lastKey2);
+ deseng.ProcessBlock(mac, 0, mac, 0);
+
+ deseng.Init(true, this.lastKey3);
+ deseng.ProcessBlock(mac, 0, mac, 0);
+ // ****
+
+ Array.Copy(mac, 0, output, outOff, macSize);
+
+ Reset();
+
+ return macSize;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void Reset()
+ {
+ Array.Clear(buf, 0, buf.Length);
+ bufOff = 0;
+
+ // reset the underlying cipher.
+ cipher.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/macs/VMPCMac.cs b/Crypto/src/crypto/macs/VMPCMac.cs
new file mode 100644
index 000000000..89916355c
--- /dev/null
+++ b/Crypto/src/crypto/macs/VMPCMac.cs
@@ -0,0 +1,173 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+ public class VmpcMac
+ : IMac
+ {
+ private byte g;
+
+ private byte n = 0;
+ private byte[] P = null;
+ private byte s = 0;
+
+ private byte[] T;
+ private byte[] workingIV;
+
+ private byte[] workingKey;
+
+ private byte x1, x2, x3, x4;
+
+ public virtual int DoFinal(byte[] output, int outOff)
+ {
+ // Execute the Post-Processing Phase
+ for (int r = 1; r < 25; r++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+
+ x4 = P[(x4 + x3 + r) & 0xff];
+ x3 = P[(x3 + x2 + r) & 0xff];
+ x2 = P[(x2 + x1 + r) & 0xff];
+ x1 = P[(x1 + s + r) & 0xff];
+ T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1);
+ T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2);
+ T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3);
+ T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4);
+ g = (byte) ((g + 4) & 0x1f);
+
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+
+ // Input T to the IV-phase of the VMPC KSA
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + T[m & 0x1f]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ // Store 20 new outputs of the VMPC Stream Cipher input table M
+ byte[] M = new byte[20];
+ for (int i = 0; i < 20; i++)
+ {
+ s = P[(s + P[i & 0xff]) & 0xff];
+ M[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+
+ byte temp = P[i & 0xff];
+ P[i & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ Array.Copy(M, 0, output, outOff, M.Length);
+ Reset();
+
+ return M.Length;
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return "VMPC-MAC"; }
+ }
+
+ public virtual int GetMacSize()
+ {
+ return 20;
+ }
+
+ public virtual void Init(ICipherParameters parameters)
+ {
+ if (!(parameters is ParametersWithIV))
+ throw new ArgumentException("VMPC-MAC Init parameters must include an IV", "parameters");
+
+ ParametersWithIV ivParams = (ParametersWithIV) parameters;
+ KeyParameter key = (KeyParameter) ivParams.Parameters;
+
+ if (!(ivParams.Parameters is KeyParameter))
+ throw new ArgumentException("VMPC-MAC Init parameters must include a key", "parameters");
+
+ this.workingIV = ivParams.GetIV();
+
+ if (workingIV == null || workingIV.Length < 1 || workingIV.Length > 768)
+ throw new ArgumentException("VMPC-MAC requires 1 to 768 bytes of IV", "parameters");
+
+ this.workingKey = key.GetKey();
+
+ Reset();
+
+ }
+
+ private void initKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ s = 0;
+ P = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ P[i] = (byte) i;
+ }
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.Length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ n = 0;
+ }
+
+ public virtual void Reset()
+ {
+ initKey(this.workingKey, this.workingIV);
+ g = x1 = x2 = x3 = x4 = n = 0;
+ T = new byte[32];
+ for (int i = 0; i < 32; i++)
+ {
+ T[i] = 0;
+ }
+ }
+
+ public virtual void Update(byte input)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ byte c = (byte) (input ^ P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]);
+
+ x4 = P[(x4 + x3) & 0xff];
+ x3 = P[(x3 + x2) & 0xff];
+ x2 = P[(x2 + x1) & 0xff];
+ x1 = P[(x1 + s + c) & 0xff];
+ T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1);
+ T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2);
+ T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3);
+ T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4);
+ g = (byte) ((g + 4) & 0x1f);
+
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+
+ public virtual void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ if ((inOff + len) > input.Length)
+ throw new DataLengthException("input buffer too short");
+
+ for (int i = 0; i < len; i++)
+ {
+ Update(input[i]);
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/CbcBlockCipher.cs b/Crypto/src/crypto/modes/CbcBlockCipher.cs
new file mode 100644
index 000000000..0bbc0cb24
--- /dev/null
+++ b/Crypto/src/crypto/modes/CbcBlockCipher.cs
@@ -0,0 +1,231 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /**
+ * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
+ */
+ public class CbcBlockCipher
+ : IBlockCipher
+ {
+ private byte[] IV, cbcV, cbcNextV;
+ private int blockSize;
+ private IBlockCipher cipher;
+ private bool encrypting;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of chaining.
+ */
+ public CbcBlockCipher(
+ IBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ this.blockSize = cipher.GetBlockSize();
+
+ this.IV = new byte[blockSize];
+ this.cbcV = new byte[blockSize];
+ this.cbcNextV = new byte[blockSize];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public IBlockCipher GetUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.encrypting = forEncryption;
+
+ if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)parameters;
+ byte[] iv = ivParam.GetIV();
+
+ if (iv.Length != blockSize)
+ {
+ throw new ArgumentException("initialisation vector must be the same length as block size");
+ }
+
+ Array.Copy(iv, 0, IV, 0, iv.Length);
+
+ parameters = ivParam.Parameters;
+ }
+
+ Reset();
+
+ cipher.Init(encrypting, parameters);
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/CBC".
+ */
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName + "/CBC"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return false; }
+ }
+
+ /**
+ * return the block size of the underlying cipher.
+ *
+ * @return the block size of the underlying cipher.
+ */
+ public int GetBlockSize()
+ {
+ return cipher.GetBlockSize();
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ return (encrypting)
+ ? EncryptBlock(input, inOff, output, outOff)
+ : DecryptBlock(input, inOff, output, outOff);
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void Reset()
+ {
+ Array.Copy(IV, 0, cbcV, 0, IV.Length);
+ Array.Clear(cbcNextV, 0, cbcNextV.Length);
+
+ cipher.Reset();
+ }
+
+ /**
+ * Do the appropriate chaining step for CBC mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int EncryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ if ((inOff + blockSize) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ /*
+ * XOR the cbcV and the input,
+ * then encrypt the cbcV
+ */
+ for (int i = 0; i < blockSize; i++)
+ {
+ cbcV[i] ^= input[inOff + i];
+ }
+
+ int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff);
+
+ /*
+ * copy ciphertext to cbcV
+ */
+ Array.Copy(outBytes, outOff, cbcV, 0, cbcV.Length);
+
+ return length;
+ }
+
+ /**
+ * Do the appropriate chaining step for CBC mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the decrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int DecryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ if ((inOff + blockSize) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ Array.Copy(input, inOff, cbcNextV, 0, blockSize);
+
+ int length = cipher.ProcessBlock(input, inOff, outBytes, outOff);
+
+ /*
+ * XOR the cbcV and the output
+ */
+ for (int i = 0; i < blockSize; i++)
+ {
+ outBytes[outOff + i] ^= cbcV[i];
+ }
+
+ /*
+ * swap the back up buffer into next position
+ */
+ byte[] tmp;
+
+ tmp = cbcV;
+ cbcV = cbcNextV;
+ cbcNextV = tmp;
+
+ return length;
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/modes/CcmBlockCipher.cs b/Crypto/src/crypto/modes/CcmBlockCipher.cs
new file mode 100644
index 000000000..abfde237e
--- /dev/null
+++ b/Crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -0,0 +1,345 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /**
+ * Implements the Counter with Cipher Block Chaining mode (CCM) detailed in
+ * NIST Special Publication 800-38C.
+ * <p>
+ * <b>Note</b>: this mode is a packet mode - it needs all the data up front.
+ * </p>
+ */
+ public class CcmBlockCipher
+ : IAeadBlockCipher
+ {
+ private static readonly int BlockSize = 16;
+
+ private readonly IBlockCipher cipher;
+ private readonly byte[] macBlock;
+ private bool forEncryption;
+ private byte[] nonce;
+ private byte[] associatedText;
+ private int macSize;
+ private ICipherParameters keyParam;
+ private readonly MemoryStream data = new MemoryStream();
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used.
+ */
+ public CcmBlockCipher(
+ IBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ this.macBlock = new byte[BlockSize];
+
+ if (cipher.GetBlockSize() != BlockSize)
+ throw new ArgumentException("cipher required with a block size of " + BlockSize + ".");
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public virtual IBlockCipher GetUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ public virtual void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+
+ if (parameters is AeadParameters)
+ {
+ AeadParameters param = (AeadParameters) parameters;
+
+ nonce = param.GetNonce();
+ associatedText = param.GetAssociatedText();
+ macSize = param.MacSize / 8;
+ keyParam = param.Key;
+ }
+ else if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV param = (ParametersWithIV) parameters;
+
+ nonce = param.GetIV();
+ associatedText = null;
+ macSize = macBlock.Length / 2;
+ keyParam = param.Parameters;
+ }
+ else
+ {
+ throw new ArgumentException("invalid parameters passed to CCM");
+ }
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return cipher.AlgorithmName + "/CCM"; }
+ }
+
+ public virtual int GetBlockSize()
+ {
+ return cipher.GetBlockSize();
+ }
+
+ public virtual int ProcessByte(
+ byte input,
+ byte[] outBytes,
+ int outOff)
+ {
+ data.WriteByte(input);
+
+ return 0;
+ }
+
+ public virtual int ProcessBytes(
+ byte[] inBytes,
+ int inOff,
+ int inLen,
+ byte[] outBytes,
+ int outOff)
+ {
+ data.Write(inBytes, inOff, inLen);
+
+ return 0;
+ }
+
+ public virtual int DoFinal(
+ byte[] outBytes,
+ int outOff)
+ {
+ byte[] text = data.ToArray();
+ byte[] enc = ProcessPacket(text, 0, text.Length);
+
+ Array.Copy(enc, 0, outBytes, outOff, enc.Length);
+
+ Reset();
+
+ return enc.Length;
+ }
+
+ public virtual void Reset()
+ {
+ cipher.Reset();
+ data.SetLength(0);
+ }
+
+ /**
+ * Returns a byte array containing the mac calculated as part of the
+ * last encrypt or decrypt operation.
+ *
+ * @return the last mac calculated.
+ */
+ public virtual byte[] GetMac()
+ {
+ byte[] mac = new byte[macSize];
+
+ Array.Copy(macBlock, 0, mac, 0, mac.Length);
+
+ return mac;
+ }
+
+ public virtual int GetUpdateOutputSize(
+ int len)
+ {
+ return 0;
+ }
+
+ public int GetOutputSize(
+ int len)
+ {
+ if (forEncryption)
+ {
+ return (int) data.Length + len + macSize;
+ }
+
+ return (int) data.Length + len - macSize;
+ }
+
+ public byte[] ProcessPacket(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ if (keyParam == null)
+ throw new InvalidOperationException("CCM cipher unitialized.");
+
+ IBlockCipher ctrCipher = new SicBlockCipher(cipher);
+ byte[] iv = new byte[BlockSize];
+ byte[] output;
+
+ iv[0] = (byte)(((15 - nonce.Length) - 1) & 0x7);
+
+ Array.Copy(nonce, 0, iv, 1, nonce.Length);
+
+ ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv));
+
+ if (forEncryption)
+ {
+ int index = inOff;
+ int outOff = 0;
+
+ output = new byte[inLen + macSize];
+
+ calculateMac(input, inOff, inLen, macBlock);
+
+ ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0); // S0
+
+ while (index < inLen - BlockSize) // S1...
+ {
+ ctrCipher.ProcessBlock(input, index, output, outOff);
+ outOff += BlockSize;
+ index += BlockSize;
+ }
+
+ byte[] block = new byte[BlockSize];
+
+ Array.Copy(input, index, block, 0, inLen - index);
+
+ ctrCipher.ProcessBlock(block, 0, block, 0);
+
+ Array.Copy(block, 0, output, outOff, inLen - index);
+
+ outOff += inLen - index;
+
+ Array.Copy(macBlock, 0, output, outOff, output.Length - outOff);
+ }
+ else
+ {
+ int index = inOff;
+ int outOff = 0;
+
+ output = new byte[inLen - macSize];
+
+ Array.Copy(input, inOff + inLen - macSize, macBlock, 0, macSize);
+
+ ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0);
+
+ for (int i = macSize; i != macBlock.Length; i++)
+ {
+ macBlock[i] = 0;
+ }
+
+ while (outOff < output.Length - BlockSize)
+ {
+ ctrCipher.ProcessBlock(input, index, output, outOff);
+ outOff += BlockSize;
+ index += BlockSize;
+ }
+
+ byte[] block = new byte[BlockSize];
+
+ Array.Copy(input, index, block, 0, output.Length - outOff);
+
+ ctrCipher.ProcessBlock(block, 0, block, 0);
+
+ Array.Copy(block, 0, output, outOff, output.Length - outOff);
+
+ byte[] calculatedMacBlock = new byte[BlockSize];
+
+ calculateMac(output, 0, output.Length, calculatedMacBlock);
+
+ if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock))
+ throw new InvalidCipherTextException("mac check in CCM failed");
+ }
+
+ return output;
+ }
+
+ private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
+ {
+ IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8);
+
+ cMac.Init(keyParam);
+
+ //
+ // build b0
+ //
+ byte[] b0 = new byte[16];
+
+ if (hasAssociatedText())
+ {
+ b0[0] |= 0x40;
+ }
+
+ b0[0] |= (byte)((((cMac.GetMacSize() - 2) / 2) & 0x7) << 3);
+
+ b0[0] |= (byte)(((15 - nonce.Length) - 1) & 0x7);
+
+ Array.Copy(nonce, 0, b0, 1, nonce.Length);
+
+ int q = dataLen;
+ int count = 1;
+ while (q > 0)
+ {
+ b0[b0.Length - count] = (byte)(q & 0xff);
+ q >>= 8;
+ count++;
+ }
+
+ cMac.BlockUpdate(b0, 0, b0.Length);
+
+ //
+ // process associated text
+ //
+ if (hasAssociatedText())
+ {
+ int extra;
+
+ if (associatedText.Length < ((1 << 16) - (1 << 8)))
+ {
+ cMac.Update((byte)(associatedText.Length >> 8));
+ cMac.Update((byte)associatedText.Length);
+
+ extra = 2;
+ }
+ else // can't go any higher than 2^32
+ {
+ cMac.Update((byte)0xff);
+ cMac.Update((byte)0xfe);
+ cMac.Update((byte)(associatedText.Length >> 24));
+ cMac.Update((byte)(associatedText.Length >> 16));
+ cMac.Update((byte)(associatedText.Length >> 8));
+ cMac.Update((byte)associatedText.Length);
+
+ extra = 6;
+ }
+
+ cMac.BlockUpdate(associatedText, 0, associatedText.Length);
+
+ extra = (extra + associatedText.Length) % 16;
+ if (extra != 0)
+ {
+ for (int i = 0; i != 16 - extra; i++)
+ {
+ cMac.Update((byte)0x00);
+ }
+ }
+ }
+
+ //
+ // add the text
+ //
+ cMac.BlockUpdate(data, dataOff, dataLen);
+
+ return cMac.DoFinal(macBlock, 0);
+ }
+
+ private bool hasAssociatedText()
+ {
+ return associatedText != null && associatedText.Length != 0;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/CfbBlockCipher.cs b/Crypto/src/crypto/modes/CfbBlockCipher.cs
new file mode 100644
index 000000000..b400a72f4
--- /dev/null
+++ b/Crypto/src/crypto/modes/CfbBlockCipher.cs
@@ -0,0 +1,218 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /**
+ * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
+ */
+ public class CfbBlockCipher
+ : IBlockCipher
+ {
+ private byte[] IV;
+ private byte[] cfbV;
+ private byte[] cfbOutV;
+ private bool encrypting;
+
+ private readonly int blockSize;
+ private readonly IBlockCipher cipher;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param blockSize the block size in bits (note: a multiple of 8)
+ */
+ public CfbBlockCipher(
+ IBlockCipher cipher,
+ int bitBlockSize)
+ {
+ this.cipher = cipher;
+ this.blockSize = bitBlockSize / 8;
+ this.IV = new byte[cipher.GetBlockSize()];
+ this.cfbV = new byte[cipher.GetBlockSize()];
+ this.cfbOutV = new byte[cipher.GetBlockSize()];
+ }
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public IBlockCipher GetUnderlyingCipher()
+ {
+ return cipher;
+ }
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.encrypting = forEncryption;
+ if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV) parameters;
+ byte[] iv = ivParam.GetIV();
+ int diff = IV.Length - iv.Length;
+ Array.Copy(iv, 0, IV, diff, iv.Length);
+ Array.Clear(IV, 0, diff);
+
+ parameters = ivParam.Parameters;
+ }
+ Reset();
+ cipher.Init(true, parameters);
+ }
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/CFB"
+ * and the block size in bits.
+ */
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return true; }
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int GetBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ return (encrypting)
+ ? EncryptBlock(input, inOff, output, outOff)
+ : DecryptBlock(input, inOff, output, outOff);
+ }
+
+ /**
+ * Do the appropriate processing for CFB mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int EncryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ if ((inOff + blockSize) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+ if ((outOff + blockSize) > outBytes.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
+ //
+ // XOR the cfbV with the plaintext producing the ciphertext
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
+ }
+ //
+ // change over the input block.
+ //
+ Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
+ Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize);
+ return blockSize;
+ }
+ /**
+ * Do the appropriate processing for CFB mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int DecryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ if ((inOff + blockSize) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+ if ((outOff + blockSize) > outBytes.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
+ //
+ // change over the input block.
+ //
+ Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
+ Array.Copy(input, inOff, cfbV, cfbV.Length - blockSize, blockSize);
+ //
+ // XOR the cfbV with the ciphertext producing the plaintext
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
+ }
+ return blockSize;
+ }
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void Reset()
+ {
+ Array.Copy(IV, 0, cfbV, 0, IV.Length);
+ cipher.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/CtsBlockCipher.cs b/Crypto/src/crypto/modes/CtsBlockCipher.cs
new file mode 100644
index 000000000..a32b49675
--- /dev/null
+++ b/Crypto/src/crypto/modes/CtsBlockCipher.cs
@@ -0,0 +1,253 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /**
+ * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
+ * be used to produce cipher text which is the same outLength as the plain text.
+ */
+ public class CtsBlockCipher
+ : BufferedBlockCipher
+ {
+ private readonly int blockSize;
+
+ /**
+ * Create a buffered block cipher that uses Cipher Text Stealing
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ */
+ public CtsBlockCipher(
+ IBlockCipher cipher)
+ {
+ // TODO Should this test for acceptable ones instead?
+ if (cipher is OfbBlockCipher || cipher is CfbBlockCipher)
+ throw new ArgumentException("CtsBlockCipher can only accept ECB, or CBC ciphers");
+
+ this.cipher = cipher;
+
+ blockSize = cipher.GetBlockSize();
+
+ buf = new byte[blockSize * 2];
+ bufOff = 0;
+ }
+
+ /**
+ * return the size of the output buffer required for an update of 'length' bytes.
+ *
+ * @param length the outLength of the input.
+ * @return the space required to accommodate a call to update
+ * with length bytes of input.
+ */
+ public override int GetUpdateOutputSize(
+ int length)
+ {
+ int total = length + bufOff;
+ int leftOver = total % buf.Length;
+
+ if (leftOver == 0)
+ {
+ return total - buf.Length;
+ }
+
+ return total - leftOver;
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of length bytes.
+ *
+ * @param length the outLength of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with length bytes of input.
+ */
+ public override int GetOutputSize(
+ int length)
+ {
+ return length + bufOff;
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessByte(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ int resultLen = 0;
+
+ if (bufOff == buf.Length)
+ {
+ resultLen = cipher.ProcessBlock(buf, 0, output, outOff);
+ Debug.Assert(resultLen == blockSize);
+
+ Array.Copy(buf, blockSize, buf, 0, blockSize);
+ bufOff = blockSize;
+ }
+
+ buf[bufOff++] = input;
+
+ return resultLen;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param length the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ if (length < 0)
+ {
+ throw new ArgumentException("Can't have a negative input outLength!");
+ }
+
+ int blockSize = GetBlockSize();
+ int outLength = GetUpdateOutputSize(length);
+
+ if (outLength > 0)
+ {
+ if ((outOff + outLength) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.Length - bufOff;
+
+ if (length > gapLen)
+ {
+ Array.Copy(input, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.ProcessBlock(buf, 0, output, outOff);
+ Array.Copy(buf, blockSize, buf, 0, blockSize);
+
+ bufOff = blockSize;
+
+ length -= gapLen;
+ inOff += gapLen;
+
+ while (length > blockSize)
+ {
+ Array.Copy(input, inOff, buf, bufOff, blockSize);
+ resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen);
+ Array.Copy(buf, blockSize, buf, 0, blockSize);
+
+ length -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ Array.Copy(input, inOff, buf, bufOff, length);
+
+ bufOff += length;
+
+ return resultLen;
+ }
+
+ /**
+ * Process the last block in the buffer.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output.
+ * @exception InvalidOperationException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if cipher text decrypts wrongly (in
+ * case the exception will never Get thrown).
+ */
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ if (bufOff + outOff > output.Length)
+ {
+ throw new DataLengthException("output buffer too small in doFinal");
+ }
+
+ int blockSize = cipher.GetBlockSize();
+ int length = bufOff - blockSize;
+ byte[] block = new byte[blockSize];
+
+ if (forEncryption)
+ {
+ cipher.ProcessBlock(buf, 0, block, 0);
+
+ if (bufOff < blockSize)
+ {
+ throw new DataLengthException("need at least one block of input for CTS");
+ }
+
+ for (int i = bufOff; i != buf.Length; i++)
+ {
+ buf[i] = block[i - blockSize];
+ }
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ buf[i] ^= block[i - blockSize];
+ }
+
+ IBlockCipher c = (cipher is CbcBlockCipher)
+ ? ((CbcBlockCipher)cipher).GetUnderlyingCipher()
+ : cipher;
+
+ c.ProcessBlock(buf, blockSize, output, outOff);
+
+ Array.Copy(block, 0, output, outOff + blockSize, length);
+ }
+ else
+ {
+ byte[] lastBlock = new byte[blockSize];
+
+ IBlockCipher c = (cipher is CbcBlockCipher)
+ ? ((CbcBlockCipher)cipher).GetUnderlyingCipher()
+ : cipher;
+
+ c.ProcessBlock(buf, 0, block, 0);
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
+ }
+
+ Array.Copy(buf, blockSize, block, 0, length);
+
+ cipher.ProcessBlock(block, 0, output, outOff);
+ Array.Copy(lastBlock, 0, output, outOff + blockSize, length);
+ }
+
+ int offset = bufOff;
+
+ Reset();
+
+ return offset;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/EAXBlockCipher.cs b/Crypto/src/crypto/modes/EAXBlockCipher.cs
new file mode 100644
index 000000000..b3016d79c
--- /dev/null
+++ b/Crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -0,0 +1,302 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /**
+ * A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and
+ * Efficiency - by M. Bellare, P. Rogaway, D. Wagner.
+ *
+ * http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
+ *
+ * EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block
+ * cipher to encrypt and authenticate data. It's on-line (the length of a
+ * message isn't needed to begin processing it), has good performances, it's
+ * simple and provably secure (provided the underlying block cipher is secure).
+ *
+ * Of course, this implementations is NOT thread-safe.
+ */
+ public class EaxBlockCipher
+ : IAeadBlockCipher
+ {
+ private enum Tag : byte { N, H, C };
+
+ private SicBlockCipher cipher;
+
+ private bool forEncryption;
+
+ private int blockSize;
+
+ private IMac mac;
+
+ private byte[] nonceMac;
+ private byte[] associatedTextMac;
+ private byte[] macBlock;
+
+ private int macSize;
+ private byte[] bufBlock;
+ private int bufOff;
+
+ /**
+ * Constructor that accepts an instance of a block cipher engine.
+ *
+ * @param cipher the engine to use
+ */
+ public EaxBlockCipher(
+ IBlockCipher cipher)
+ {
+ blockSize = cipher.GetBlockSize();
+ mac = new CMac(cipher);
+ macBlock = new byte[blockSize];
+ bufBlock = new byte[blockSize * 2];
+ associatedTextMac = new byte[mac.GetMacSize()];
+ nonceMac = new byte[mac.GetMacSize()];
+ this.cipher = new SicBlockCipher(cipher);
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; }
+ }
+
+ public virtual int GetBlockSize()
+ {
+ return cipher.GetBlockSize();
+ }
+
+ public virtual void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+
+ byte[] nonce, associatedText;
+ ICipherParameters keyParam;
+
+ if (parameters is AeadParameters)
+ {
+ AeadParameters param = (AeadParameters) parameters;
+
+ nonce = param.GetNonce();
+ associatedText = param.GetAssociatedText();
+ macSize = param.MacSize / 8;
+ keyParam = param.Key;
+ }
+ else if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV param = (ParametersWithIV) parameters;
+
+ nonce = param.GetIV();
+ associatedText = new byte[0];
+ macSize = mac.GetMacSize() / 2;
+ keyParam = param.Parameters;
+ }
+ else
+ {
+ throw new ArgumentException("invalid parameters passed to EAX");
+ }
+
+ byte[] tag = new byte[blockSize];
+
+ mac.Init(keyParam);
+ tag[blockSize - 1] = (byte) Tag.H;
+ mac.BlockUpdate(tag, 0, blockSize);
+ mac.BlockUpdate(associatedText, 0, associatedText.Length);
+ mac.DoFinal(associatedTextMac, 0);
+
+ tag[blockSize - 1] = (byte) Tag.N;
+ mac.BlockUpdate(tag, 0, blockSize);
+ mac.BlockUpdate(nonce, 0, nonce.Length);
+ mac.DoFinal(nonceMac, 0);
+
+ tag[blockSize - 1] = (byte) Tag.C;
+ mac.BlockUpdate(tag, 0, blockSize);
+
+ cipher.Init(true, new ParametersWithIV(keyParam, nonceMac));
+ }
+
+ private void calculateMac()
+ {
+ byte[] outC = new byte[blockSize];
+ mac.DoFinal(outC, 0);
+
+ for (int i = 0; i < macBlock.Length; i++)
+ {
+ macBlock[i] = (byte)(nonceMac[i] ^ associatedTextMac[i] ^ outC[i]);
+ }
+ }
+
+ public virtual void Reset()
+ {
+ Reset(true);
+ }
+
+ private void Reset(
+ bool clearMac)
+ {
+ cipher.Reset();
+ mac.Reset();
+
+ bufOff = 0;
+ Array.Clear(bufBlock, 0, bufBlock.Length);
+
+ if (clearMac)
+ {
+ Array.Clear(macBlock, 0, macBlock.Length);
+ }
+
+ byte[] tag = new byte[blockSize];
+ tag[blockSize - 1] = (byte) Tag.C;
+ mac.BlockUpdate(tag, 0, blockSize);
+ }
+
+ public virtual int ProcessByte(
+ byte input,
+ byte[] outBytes,
+ int outOff)
+ {
+ return process(input, outBytes, outOff);
+ }
+
+ public virtual int ProcessBytes(
+ byte[] inBytes,
+ int inOff,
+ int len,
+ byte[] outBytes,
+ int outOff)
+ {
+ int resultLen = 0;
+
+ for (int i = 0; i != len; i++)
+ {
+ resultLen += process(inBytes[inOff + i], outBytes, outOff + resultLen);
+ }
+
+ return resultLen;
+ }
+
+ public virtual int DoFinal(
+ byte[] outBytes,
+ int outOff)
+ {
+ int extra = bufOff;
+ byte[] tmp = new byte[bufBlock.Length];
+
+ bufOff = 0;
+
+ if (forEncryption)
+ {
+ cipher.ProcessBlock(bufBlock, 0, tmp, 0);
+ cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize);
+
+ Array.Copy(tmp, 0, outBytes, outOff, extra);
+
+ mac.BlockUpdate(tmp, 0, extra);
+
+ calculateMac();
+
+ Array.Copy(macBlock, 0, outBytes, outOff + extra, macSize);
+
+ Reset(false);
+
+ return extra + macSize;
+ }
+ else
+ {
+ if (extra > macSize)
+ {
+ mac.BlockUpdate(bufBlock, 0, extra - macSize);
+
+ cipher.ProcessBlock(bufBlock, 0, tmp, 0);
+ cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize);
+
+ Array.Copy(tmp, 0, outBytes, outOff, extra - macSize);
+ }
+
+ calculateMac();
+
+ if (!verifyMac(bufBlock, extra - macSize))
+ throw new InvalidCipherTextException("mac check in EAX failed");
+
+ Reset(false);
+
+ return extra - macSize;
+ }
+ }
+
+ public virtual byte[] GetMac()
+ {
+ byte[] mac = new byte[macSize];
+
+ Array.Copy(macBlock, 0, mac, 0, macSize);
+
+ return mac;
+ }
+
+ public virtual int GetUpdateOutputSize(
+ int len)
+ {
+ return ((len + bufOff) / blockSize) * blockSize;
+ }
+
+ public virtual int GetOutputSize(
+ int len)
+ {
+ if (forEncryption)
+ {
+ return len + bufOff + macSize;
+ }
+
+ return len + bufOff - macSize;
+ }
+
+ private int process(
+ byte b,
+ byte[] outBytes,
+ int outOff)
+ {
+ bufBlock[bufOff++] = b;
+
+ if (bufOff == bufBlock.Length)
+ {
+ int size;
+
+ if (forEncryption)
+ {
+ size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff);
+
+ mac.BlockUpdate(outBytes, outOff, blockSize);
+ }
+ else
+ {
+ mac.BlockUpdate(bufBlock, 0, blockSize);
+
+ size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff);
+ }
+
+ bufOff = blockSize;
+ Array.Copy(bufBlock, blockSize, bufBlock, 0, blockSize);
+
+ return size;
+ }
+
+ return 0;
+ }
+
+ private bool verifyMac(byte[] mac, int off)
+ {
+ for (int i = 0; i < macSize; i++)
+ {
+ if (macBlock[i] != mac[off + i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/GCMBlockCipher.cs b/Crypto/src/crypto/modes/GCMBlockCipher.cs
new file mode 100644
index 000000000..6a3a4463d
--- /dev/null
+++ b/Crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -0,0 +1,400 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Modes.Gcm;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /// <summary>
+ /// Implements the Galois/Counter mode (GCM) detailed in
+ /// NIST Special Publication 800-38D.
+ /// </summary>
+ public class GcmBlockCipher
+ : IAeadBlockCipher
+ {
+ private const int BlockSize = 16;
+ private static readonly byte[] Zeroes = new byte[BlockSize];
+
+ private readonly IBlockCipher cipher;
+ private readonly IGcmMultiplier multiplier;
+
+ // These fields are set by Init and not modified by processing
+ private bool forEncryption;
+ private int macSize;
+ private byte[] nonce;
+ private byte[] A;
+ private KeyParameter keyParam;
+ private byte[] H;
+ private byte[] initS;
+ private byte[] J0;
+
+ // These fields are modified during processing
+ private byte[] bufBlock;
+ private byte[] macBlock;
+ private byte[] S;
+ private byte[] counter;
+ private int bufOff;
+ private ulong totalLength;
+
+ public GcmBlockCipher(
+ IBlockCipher c)
+ : this(c, null)
+ {
+ }
+
+ public GcmBlockCipher(
+ IBlockCipher c,
+ IGcmMultiplier m)
+ {
+ if (c.GetBlockSize() != BlockSize)
+ throw new ArgumentException("cipher required with a block size of " + BlockSize + ".");
+
+ if (m == null)
+ {
+ // TODO Consider a static property specifying default multiplier
+ m = new Tables8kGcmMultiplier();
+ }
+
+ this.cipher = c;
+ this.multiplier = m;
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return cipher.AlgorithmName + "/GCM"; }
+ }
+
+ public virtual int GetBlockSize()
+ {
+ return BlockSize;
+ }
+
+ public virtual void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+ this.macBlock = null;
+
+ if (parameters is AeadParameters)
+ {
+ AeadParameters param = (AeadParameters)parameters;
+
+ nonce = param.GetNonce();
+ A = param.GetAssociatedText();
+
+ int macSizeBits = param.MacSize;
+ if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0)
+ {
+ throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
+ }
+
+ macSize = macSizeBits / 8;
+ keyParam = param.Key;
+ }
+ else if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV param = (ParametersWithIV)parameters;
+
+ nonce = param.GetIV();
+ A = null;
+ macSize = 16;
+ keyParam = (KeyParameter)param.Parameters;
+ }
+ else
+ {
+ throw new ArgumentException("invalid parameters passed to GCM");
+ }
+
+ int bufLength = forEncryption ? BlockSize : (BlockSize + macSize);
+ this.bufBlock = new byte[bufLength];
+
+ if (nonce == null || nonce.Length < 1)
+ {
+ throw new ArgumentException("IV must be at least 1 byte");
+ }
+
+ if (A == null)
+ {
+ // Avoid lots of null checks
+ A = new byte[0];
+ }
+
+ // Cipher always used in forward mode
+ cipher.Init(true, keyParam);
+
+ // TODO This should be configurable by Init parameters
+ // (but must be 16 if nonce length not 12) (BlockSize?)
+// this.tagLength = 16;
+
+ this.H = new byte[BlockSize];
+ cipher.ProcessBlock(H, 0, H, 0);
+ multiplier.Init(H);
+
+ this.initS = gHASH(A);
+
+ if (nonce.Length == 12)
+ {
+ this.J0 = new byte[16];
+ Array.Copy(nonce, 0, J0, 0, nonce.Length);
+ this.J0[15] = 0x01;
+ }
+ else
+ {
+ this.J0 = gHASH(nonce);
+ byte[] X = new byte[16];
+ packLength((ulong)nonce.Length * 8UL, X, 8);
+ GcmUtilities.Xor(this.J0, X);
+ multiplier.MultiplyH(this.J0);
+ }
+
+ this.S = Arrays.Clone(initS);
+ this.counter = Arrays.Clone(J0);
+ this.bufOff = 0;
+ this.totalLength = 0;
+ }
+
+ public virtual byte[] GetMac()
+ {
+ return Arrays.Clone(macBlock);
+ }
+
+ public virtual int GetOutputSize(
+ int len)
+ {
+ if (forEncryption)
+ {
+ return len + bufOff + macSize;
+ }
+
+ return len + bufOff - macSize;
+ }
+
+ public virtual int GetUpdateOutputSize(
+ int len)
+ {
+ return ((len + bufOff) / BlockSize) * BlockSize;
+ }
+
+ public virtual int ProcessByte(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ return Process(input, output, outOff);
+ }
+
+ public virtual int ProcessBytes(
+ byte[] input,
+ int inOff,
+ int len,
+ byte[] output,
+ int outOff)
+ {
+ int resultLen = 0;
+
+ for (int i = 0; i != len; i++)
+ {
+// resultLen += Process(input[inOff + i], output, outOff + resultLen);
+ bufBlock[bufOff++] = input[inOff + i];
+
+ if (bufOff == bufBlock.Length)
+ {
+ gCTRBlock(bufBlock, BlockSize, output, outOff + resultLen);
+ if (!forEncryption)
+ {
+ Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
+ }
+// bufOff = 0;
+ bufOff = bufBlock.Length - BlockSize;
+// return bufBlock.Length;
+ resultLen += BlockSize;
+ }
+ }
+
+ return resultLen;
+ }
+
+ private int Process(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ bufBlock[bufOff++] = input;
+
+ if (bufOff == bufBlock.Length)
+ {
+ gCTRBlock(bufBlock, BlockSize, output, outOff);
+ if (!forEncryption)
+ {
+ Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
+ }
+ // bufOff = 0;
+ bufOff = bufBlock.Length - BlockSize;
+ // return bufBlock.Length;
+ return BlockSize;
+ }
+
+ return 0;
+ }
+
+ public int DoFinal(byte[] output, int outOff)
+ {
+ int extra = bufOff;
+ if (!forEncryption)
+ {
+ if (extra < macSize)
+ throw new InvalidCipherTextException("data too short");
+
+ extra -= macSize;
+ }
+
+ if (extra > 0)
+ {
+ byte[] tmp = new byte[BlockSize];
+ Array.Copy(bufBlock, 0, tmp, 0, extra);
+ gCTRBlock(tmp, extra, output, outOff);
+ }
+
+ // Final gHASH
+ byte[] X = new byte[16];
+ packLength((ulong)A.Length * 8UL, X, 0);
+ packLength(totalLength * 8UL, X, 8);
+
+ GcmUtilities.Xor(S, X);
+ multiplier.MultiplyH(S);
+
+ // TODO Fix this if tagLength becomes configurable
+ // T = MSBt(GCTRk(J0,S))
+ byte[] tag = new byte[BlockSize];
+ cipher.ProcessBlock(J0, 0, tag, 0);
+ GcmUtilities.Xor(tag, S);
+
+ int resultLen = extra;
+
+ // We place into macBlock our calculated value for T
+ this.macBlock = new byte[macSize];
+ Array.Copy(tag, 0, macBlock, 0, macSize);
+
+ if (forEncryption)
+ {
+ // Append T to the message
+ Array.Copy(macBlock, 0, output, outOff + bufOff, macSize);
+ resultLen += macSize;
+ }
+ else
+ {
+ // Retrieve the T value from the message and compare to calculated one
+ byte[] msgMac = new byte[macSize];
+ Array.Copy(bufBlock, extra, msgMac, 0, macSize);
+ if (!Arrays.ConstantTimeAreEqual(this.macBlock, msgMac))
+ throw new InvalidCipherTextException("mac check in GCM failed");
+ }
+
+ Reset(false);
+
+ return resultLen;
+ }
+
+ public virtual void Reset()
+ {
+ Reset(true);
+ }
+
+ private void Reset(
+ bool clearMac)
+ {
+ S = Arrays.Clone(initS);
+ counter = Arrays.Clone(J0);
+ bufOff = 0;
+ totalLength = 0;
+
+ if (bufBlock != null)
+ {
+ Array.Clear(bufBlock, 0, bufBlock.Length);
+ }
+
+ if (clearMac)
+ {
+ macBlock = null;
+ }
+
+ cipher.Reset();
+ }
+
+ private void gCTRBlock(byte[] buf, int bufCount, byte[] output, int outOff)
+ {
+// inc(counter);
+ for (int i = 15; i >= 12; --i)
+ {
+ if (++counter[i] != 0) break;
+ }
+
+ byte[] tmp = new byte[BlockSize];
+ cipher.ProcessBlock(counter, 0, tmp, 0);
+
+ byte[] hashBytes;
+ if (forEncryption)
+ {
+ Array.Copy(Zeroes, bufCount, tmp, bufCount, BlockSize - bufCount);
+ hashBytes = tmp;
+ }
+ else
+ {
+ hashBytes = buf;
+ }
+
+ for (int i = bufCount - 1; i >= 0; --i)
+ {
+ tmp[i] ^= buf[i];
+ output[outOff + i] = tmp[i];
+ }
+
+// gHASHBlock(hashBytes);
+ GcmUtilities.Xor(S, hashBytes);
+ multiplier.MultiplyH(S);
+
+ totalLength += (ulong)bufCount;
+ }
+
+ private byte[] gHASH(byte[] b)
+ {
+ byte[] Y = new byte[16];
+
+ for (int pos = 0; pos < b.Length; pos += 16)
+ {
+ byte[] X = new byte[16];
+ int num = System.Math.Min(b.Length - pos, 16);
+ Array.Copy(b, pos, X, 0, num);
+ GcmUtilities.Xor(Y, X);
+ multiplier.MultiplyH(Y);
+ }
+
+ return Y;
+ }
+
+// private void gHASHBlock(byte[] block)
+// {
+// GcmUtilities.Xor(S, block);
+// multiplier.MultiplyH(S);
+// }
+
+// private static void inc(byte[] block)
+// {
+// for (int i = 15; i >= 12; --i)
+// {
+// if (++block[i] != 0) break;
+// }
+// }
+
+ private static void packLength(ulong len, byte[] bs, int off)
+ {
+ Pack.UInt32_To_BE((uint)(len >> 32), bs, off);
+ Pack.UInt32_To_BE((uint)len, bs, off + 4);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/modes/GOFBBlockCipher.cs b/Crypto/src/crypto/modes/GOFBBlockCipher.cs
new file mode 100644
index 000000000..7db843115
--- /dev/null
+++ b/Crypto/src/crypto/modes/GOFBBlockCipher.cs
@@ -0,0 +1,223 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /**
+ * implements the GOST 28147 OFB counter mode (GCTR).
+ */
+ public class GOfbBlockCipher
+ : IBlockCipher
+ {
+ private byte[] IV;
+ private byte[] ofbV;
+ private byte[] ofbOutV;
+
+ private readonly int blockSize;
+ private readonly IBlockCipher cipher;
+
+ bool firstStep = true;
+ int N3;
+ int N4;
+ const int C1 = 16843012; //00000001000000010000000100000100
+ const int C2 = 16843009; //00000001000000010000000100000001
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * counter mode (must have a 64 bit block size).
+ */
+ public GOfbBlockCipher(
+ IBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ this.blockSize = cipher.GetBlockSize();
+
+ if (blockSize != 8)
+ {
+ throw new ArgumentException("GCTR only for 64 bit block ciphers");
+ }
+
+ this.IV = new byte[cipher.GetBlockSize()];
+ this.ofbV = new byte[cipher.GetBlockSize()];
+ this.ofbOutV = new byte[cipher.GetBlockSize()];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public IBlockCipher GetUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param encrypting if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param parameters the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is inappropriate.
+ */
+ public void Init(
+ bool forEncryption, //ignored by this CTR mode
+ ICipherParameters parameters)
+ {
+ firstStep = true;
+ N3 = 0;
+ N4 = 0;
+
+ if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)parameters;
+ byte[] iv = ivParam.GetIV();
+
+ if (iv.Length < IV.Length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
+ for (int i = 0; i < IV.Length - iv.Length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ Array.Copy(iv, 0, IV, 0, IV.Length);
+ }
+
+ parameters = ivParam.Parameters;
+ }
+
+ Reset();
+
+ cipher.Init(true, parameters);
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/GCTR"
+ * and the block size in bits
+ */
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName + "/GCTR"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return true; }
+ }
+
+ /**
+ * return the block size we are operating at (in bytes).
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int GetBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if ((inOff + blockSize) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (firstStep)
+ {
+ firstStep = false;
+ cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
+ N3 = bytesToint(ofbOutV, 0);
+ N4 = bytesToint(ofbOutV, 4);
+ }
+ N3 += C2;
+ N4 += C1;
+ intTobytes(N3, ofbV, 0);
+ intTobytes(N4, ofbV, 4);
+
+ cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
+
+ //
+ // XOR the ofbV with the plaintext producing the cipher text (and
+ // the next input block).
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize);
+ Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * reset the feedback vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void Reset()
+ {
+ Array.Copy(IV, 0, ofbV, 0, IV.Length);
+
+ cipher.Reset();
+ }
+
+ //array of bytes to type int
+ private int bytesToint(
+ byte[] inBytes,
+ int inOff)
+ {
+ return (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) +
+ ((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff);
+ }
+
+ //int to array of bytes
+ private void intTobytes(
+ int num,
+ byte[] outBytes,
+ int outOff)
+ {
+ outBytes[outOff + 3] = (byte)(num >> 24);
+ outBytes[outOff + 2] = (byte)(num >> 16);
+ outBytes[outOff + 1] = (byte)(num >> 8);
+ outBytes[outOff] = (byte)num;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/IAeadBlockCipher.cs b/Crypto/src/crypto/modes/IAeadBlockCipher.cs
new file mode 100644
index 000000000..ca7dab44c
--- /dev/null
+++ b/Crypto/src/crypto/modes/IAeadBlockCipher.cs
@@ -0,0 +1,90 @@
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /// <summary>
+ /// A block cipher mode that includes authenticated encryption with a streaming mode
+ /// and optional associated data.</summary>
+ /// <see cref="AeadParameters"/>
+ public interface IAeadBlockCipher
+ {
+ /// <summary>The name of the algorithm this cipher implements.</summary>
+ string AlgorithmName { get; }
+
+ /// <summary>Initialise the cipher.</summary>
+ /// <remarks>Parameter can either be an AeadParameters or a ParametersWithIV object.</remarks>
+ /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
+ /// <param name="parameters">The key or other data required by the cipher.</param>
+ void Init(bool forEncryption, ICipherParameters parameters);
+
+ /// <returns>The block size for this cipher, in bytes.</returns>
+ int GetBlockSize();
+
+ /**
+ * Encrypt/decrypt a single byte.
+ *
+ * @param input the byte to be processed.
+ * @param outBytes the output buffer the processed byte goes into.
+ * @param outOff the offset into the output byte array the processed data starts at.
+ * @return the number of bytes written to out.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ int ProcessByte(byte input, byte[] outBytes, int outOff);
+
+ /**
+ * Process a block of bytes from in putting the result into out.
+ *
+ * @param inBytes the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ * @param outBytes the output buffer the processed bytes go into.
+ * @param outOff the offset into the output byte array the processed data starts at.
+ * @return the number of bytes written to out.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff);
+
+ /**
+ * Finish the operation either appending or verifying the MAC at the end of the data.
+ *
+ * @param outBytes space for any resulting output data.
+ * @param outOff offset into out to start copying the data at.
+ * @return number of bytes written into out.
+ * @throws InvalidOperationException if the cipher is in an inappropriate state.
+ * @throws InvalidCipherTextException if the MAC fails to match.
+ */
+ int DoFinal(byte[] outBytes, int outOff);
+
+ /**
+ * Return the value of the MAC associated with the last stream processed.
+ *
+ * @return MAC for plaintext data.
+ */
+ byte[] GetMac();
+
+ /**
+ * Return the size of the output buffer required for a ProcessBytes
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to ProcessBytes
+ * with len bytes of input.
+ */
+ int GetUpdateOutputSize(int len);
+
+ /**
+ * Return the size of the output buffer required for a ProcessBytes plus a
+ * DoFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to ProcessBytes and DoFinal
+ * with len bytes of input.
+ */
+ int GetOutputSize(int len);
+
+ /// <summary>
+ /// Reset the cipher to the same state as it was after the last init (if there was one).
+ /// </summary>
+ void Reset();
+ }
+}
diff --git a/Crypto/src/crypto/modes/OfbBlockCipher.cs b/Crypto/src/crypto/modes/OfbBlockCipher.cs
new file mode 100644
index 000000000..9408a74d4
--- /dev/null
+++ b/Crypto/src/crypto/modes/OfbBlockCipher.cs
@@ -0,0 +1,178 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /**
+ * implements a Output-FeedBack (OFB) mode on top of a simple cipher.
+ */
+ public class OfbBlockCipher
+ : IBlockCipher
+ {
+ private byte[] IV;
+ private byte[] ofbV;
+ private byte[] ofbOutV;
+
+ private readonly int blockSize;
+ private readonly IBlockCipher cipher;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ * @param blockSize the block size in bits (note: a multiple of 8)
+ */
+ public OfbBlockCipher(
+ IBlockCipher cipher,
+ int blockSize)
+ {
+ this.cipher = cipher;
+ this.blockSize = blockSize / 8;
+
+ this.IV = new byte[cipher.GetBlockSize()];
+ this.ofbV = new byte[cipher.GetBlockSize()];
+ this.ofbOutV = new byte[cipher.GetBlockSize()];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public IBlockCipher GetUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption, //ignored by this OFB mode
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)parameters;
+ byte[] iv = ivParam.GetIV();
+
+ if (iv.Length < IV.Length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
+ for (int i = 0; i < IV.Length - iv.Length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ Array.Copy(iv, 0, IV, 0, IV.Length);
+ }
+
+ parameters = ivParam.Parameters;
+ }
+
+ Reset();
+
+ cipher.Init(true, parameters);
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/OFB"
+ * and the block size in bits
+ */
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName + "/OFB" + (blockSize * 8); }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return true; }
+ }
+
+ /**
+ * return the block size we are operating at (in bytes).
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int GetBlockSize()
+ {
+ return blockSize;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ if ((inOff + blockSize) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
+
+ //
+ // XOR the ofbV with the plaintext producing the cipher text (and
+ // the next input block).
+ //
+ for (int i = 0; i < blockSize; i++)
+ {
+ output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]);
+ }
+
+ //
+ // change over the input block.
+ //
+ Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize);
+ Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize);
+
+ return blockSize;
+ }
+
+ /**
+ * reset the feedback vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void Reset()
+ {
+ Array.Copy(IV, 0, ofbV, 0, IV.Length);
+
+ cipher.Reset();
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs b/Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs
new file mode 100644
index 000000000..038ca783d
--- /dev/null
+++ b/Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs
@@ -0,0 +1,337 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /**
+ * Implements OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode
+ * on top of a simple cipher. This class assumes the IV has been prepended
+ * to the data stream already, and just accomodates the reset after
+ * (blockSize + 2) bytes have been read.
+ * <p>
+ * For further info see <a href="http://www.ietf.org/rfc/rfc2440.html">RFC 2440</a>.
+ * </p>
+ */
+ public class OpenPgpCfbBlockCipher
+ : IBlockCipher
+ {
+ private byte[] IV;
+ private byte[] FR;
+ private byte[] FRE;
+
+ private readonly IBlockCipher cipher;
+ private readonly int blockSize;
+
+ private int count;
+ private bool forEncryption;
+
+ /**
+ * Basic constructor.
+ *
+ * @param cipher the block cipher to be used as the basis of the
+ * feedback mode.
+ */
+ public OpenPgpCfbBlockCipher(
+ IBlockCipher cipher)
+ {
+ this.cipher = cipher;
+
+ this.blockSize = cipher.GetBlockSize();
+ this.IV = new byte[blockSize];
+ this.FR = new byte[blockSize];
+ this.FRE = new byte[blockSize];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public IBlockCipher GetUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ /**
+ * return the algorithm name and mode.
+ *
+ * @return the name of the underlying algorithm followed by "/PGPCFB"
+ * and the block size in bits.
+ */
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName + "/OpenPGPCFB"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return true; }
+ }
+
+ /**
+ * return the block size we are operating at.
+ *
+ * @return the block size we are operating at (in bytes).
+ */
+ public int GetBlockSize()
+ {
+ return cipher.GetBlockSize();
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) : DecryptBlock(input, inOff, output, outOff);
+ }
+
+ /**
+ * reset the chaining vector back to the IV and reset the underlying
+ * cipher.
+ */
+ public void Reset()
+ {
+ count = 0;
+
+ Array.Copy(IV, 0, FR, 0, FR.Length);
+
+ cipher.Reset();
+ }
+
+ /**
+ * Initialise the cipher and, possibly, the initialisation vector (IV).
+ * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+ * An IV which is too short is handled in FIPS compliant fashion.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param parameters the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+
+ if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV)parameters;
+ byte[] iv = ivParam.GetIV();
+
+ if (iv.Length < IV.Length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
+ for (int i = 0; i < IV.Length - iv.Length; i++)
+ {
+ IV[i] = 0;
+ }
+ }
+ else
+ {
+ Array.Copy(iv, 0, IV, 0, IV.Length);
+ }
+
+ parameters = ivParam.Parameters;
+ }
+
+ Reset();
+
+ cipher.Init(true, parameters);
+ }
+
+ /**
+ * Encrypt one byte of data according to CFB mode.
+ * @param data the byte to encrypt
+ * @param blockOff offset in the current block
+ * @returns the encrypted byte
+ */
+ private byte EncryptByte(byte data, int blockOff)
+ {
+ return (byte)(FRE[blockOff] ^ data);
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode encryption.
+ *
+ * @param in the array containing the data to be encrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int EncryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ if ((inOff + blockSize) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > outBytes.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (count > blockSize)
+ {
+ FR[blockSize - 2] = outBytes[outOff] = EncryptByte(input[inOff], blockSize - 2);
+ FR[blockSize - 1] = outBytes[outOff + 1] = EncryptByte(input[inOff + 1], blockSize - 1);
+
+ cipher.ProcessBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
+ }
+ }
+ else if (count == 0)
+ {
+ cipher.ProcessBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n);
+ }
+
+ count += blockSize;
+ }
+ else if (count == blockSize)
+ {
+ cipher.ProcessBlock(FR, 0, FRE, 0);
+
+ outBytes[outOff] = EncryptByte(input[inOff], 0);
+ outBytes[outOff + 1] = EncryptByte(input[inOff + 1], 1);
+
+ //
+ // do reset
+ //
+ Array.Copy(FR, 2, FR, 0, blockSize - 2);
+ Array.Copy(outBytes, outOff, FR, blockSize - 2, 2);
+
+ cipher.ProcessBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
+ }
+
+ count += blockSize;
+ }
+
+ return blockSize;
+ }
+
+ /**
+ * Do the appropriate processing for CFB IV mode decryption.
+ *
+ * @param in the array containing the data to be decrypted.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the encrypted data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ private int DecryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] outBytes,
+ int outOff)
+ {
+ if ((inOff + blockSize) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > outBytes.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
+ if (count > blockSize)
+ {
+ byte inVal = input[inOff];
+ FR[blockSize - 2] = inVal;
+ outBytes[outOff] = EncryptByte(inVal, blockSize - 2);
+
+ inVal = input[inOff + 1];
+ FR[blockSize - 1] = inVal;
+ outBytes[outOff + 1] = EncryptByte(inVal, blockSize - 1);
+
+ cipher.ProcessBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ inVal = input[inOff + n];
+ FR[n - 2] = inVal;
+ outBytes[outOff + n] = EncryptByte(inVal, n - 2);
+ }
+ }
+ else if (count == 0)
+ {
+ cipher.ProcessBlock(FR, 0, FRE, 0);
+
+ for (int n = 0; n < blockSize; n++)
+ {
+ FR[n] = input[inOff + n];
+ outBytes[n] = EncryptByte(input[inOff + n], n);
+ }
+
+ count += blockSize;
+ }
+ else if (count == blockSize)
+ {
+ cipher.ProcessBlock(FR, 0, FRE, 0);
+
+ byte inVal1 = input[inOff];
+ byte inVal2 = input[inOff + 1];
+ outBytes[outOff ] = EncryptByte(inVal1, 0);
+ outBytes[outOff + 1] = EncryptByte(inVal2, 1);
+
+ Array.Copy(FR, 2, FR, 0, blockSize - 2);
+
+ FR[blockSize - 2] = inVal1;
+ FR[blockSize - 1] = inVal2;
+
+ cipher.ProcessBlock(FR, 0, FRE, 0);
+
+ for (int n = 2; n < blockSize; n++)
+ {
+ byte inVal = input[inOff + n];
+ FR[n - 2] = inVal;
+ outBytes[outOff + n] = EncryptByte(inVal, n - 2);
+ }
+
+ count += blockSize;
+ }
+
+ return blockSize;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/SicBlockCipher.cs b/Crypto/src/crypto/modes/SicBlockCipher.cs
new file mode 100644
index 000000000..c45026e82
--- /dev/null
+++ b/Crypto/src/crypto/modes/SicBlockCipher.cs
@@ -0,0 +1,110 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+ /**
+ * Implements the Segmented Integer Counter (SIC) mode on top of a simple
+ * block cipher.
+ */
+ public class SicBlockCipher
+ : IBlockCipher
+ {
+ private readonly IBlockCipher cipher;
+ private readonly int blockSize;
+ private readonly byte[] IV;
+ private readonly byte[] counter;
+ private readonly byte[] counterOut;
+
+ /**
+ * Basic constructor.
+ *
+ * @param c the block cipher to be used.
+ */
+ public SicBlockCipher(IBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ this.blockSize = cipher.GetBlockSize();
+ this.IV = new byte[blockSize];
+ this.counter = new byte[blockSize];
+ this.counterOut = new byte[blockSize];
+ }
+
+ /**
+ * return the underlying block cipher that we are wrapping.
+ *
+ * @return the underlying block cipher that we are wrapping.
+ */
+ public IBlockCipher GetUnderlyingCipher()
+ {
+ return cipher;
+ }
+
+ public void Init(
+ bool forEncryption, //ignored by this CTR mode
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV ivParam = (ParametersWithIV) parameters;
+ byte[] iv = ivParam.GetIV();
+ Array.Copy(iv, 0, IV, 0, IV.Length);
+
+ Reset();
+ cipher.Init(true, ivParam.Parameters);
+ }
+ else
+ {
+ throw new ArgumentException("SIC mode requires ParametersWithIV", "parameters");
+ }
+ }
+
+ public string AlgorithmName
+ {
+ get { return cipher.AlgorithmName + "/SIC"; }
+ }
+
+ public bool IsPartialBlockOkay
+ {
+ get { return true; }
+ }
+
+ public int GetBlockSize()
+ {
+ return cipher.GetBlockSize();
+ }
+
+ public int ProcessBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ cipher.ProcessBlock(counter, 0, counterOut, 0);
+
+ //
+ // XOR the counterOut with the plaintext producing the cipher text
+ //
+ for (int i = 0; i < counterOut.Length; i++)
+ {
+ output[outOff + i] = (byte)(counterOut[i] ^ input[inOff + i]);
+ }
+
+ // Increment the counter
+ int j = counter.Length;
+ while (--j >= 0 && ++counter[j] == 0)
+ {
+ }
+
+ return counter.Length;
+ }
+
+ public void Reset()
+ {
+ Array.Copy(IV, 0, counter, 0, counter.Length);
+ cipher.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs b/Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs
new file mode 100644
index 000000000..98049e1db
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs
@@ -0,0 +1,40 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+ public class BasicGcmExponentiator
+ : IGcmExponentiator
+ {
+ private byte[] x;
+
+ public void Init(byte[] x)
+ {
+ this.x = Arrays.Clone(x);
+ }
+
+ public void ExponentiateX(long pow, byte[] output)
+ {
+ // Initial value is little-endian 1
+ byte[] y = GcmUtilities.OneAsBytes();
+
+ if (pow > 0)
+ {
+ byte[] powX = Arrays.Clone(x);
+ do
+ {
+ if ((pow & 1L) != 0)
+ {
+ GcmUtilities.Multiply(y, powX);
+ }
+ GcmUtilities.Multiply(powX, powX);
+ pow >>= 1;
+ }
+ while (pow > 0);
+ }
+
+ Array.Copy(y, 0, output, 0, 16);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs
new file mode 100644
index 000000000..4076de990
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+ public class BasicGcmMultiplier
+ : IGcmMultiplier
+ {
+ private byte[] H;
+
+ public void Init(byte[] H)
+ {
+ this.H = (byte[])H.Clone();
+ }
+
+ public void MultiplyH(byte[] x)
+ {
+ GcmUtilities.Multiply(x, H);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/gcm/GcmUtilities.cs b/Crypto/src/crypto/modes/gcm/GcmUtilities.cs
new file mode 100644
index 000000000..8da125641
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/GcmUtilities.cs
@@ -0,0 +1,149 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+ internal abstract class GcmUtilities
+ {
+ internal static byte[] OneAsBytes()
+ {
+ byte[] tmp = new byte[16];
+ tmp[0] = 0x80;
+ return tmp;
+ }
+
+ internal static uint[] OneAsUints()
+ {
+ uint[] tmp = new uint[4];
+ tmp[0] = 0x80000000;
+ return tmp;
+ }
+
+ internal static uint[] AsUints(byte[] bs)
+ {
+ uint[] us = new uint[4];
+ us[0] = Pack.BE_To_UInt32(bs, 0);
+ us[1] = Pack.BE_To_UInt32(bs, 4);
+ us[2] = Pack.BE_To_UInt32(bs, 8);
+ us[3] = Pack.BE_To_UInt32(bs, 12);
+ return us;
+ }
+
+ internal static void Multiply(byte[] block, byte[] val)
+ {
+ byte[] tmp = Arrays.Clone(block);
+ byte[] c = new byte[16];
+
+ for (int i = 0; i < 16; ++i)
+ {
+ byte bits = val[i];
+ for (int j = 7; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ Xor(c, tmp);
+ }
+
+ bool lsb = (tmp[15] & 1) != 0;
+ ShiftRight(tmp);
+ if (lsb)
+ {
+ // R = new byte[]{ 0xe1, ... };
+ //GCMUtilities.Xor(tmp, R);
+ tmp[0] ^= (byte)0xe1;
+ }
+ }
+ }
+
+ Array.Copy(c, 0, block, 0, 16);
+ }
+
+ // P is the value with only bit i=1 set
+ internal static void MultiplyP(uint[] x)
+ {
+ bool lsb = (x[3] & 1) != 0;
+ ShiftRight(x);
+ if (lsb)
+ {
+ // R = new uint[]{ 0xe1000000, 0, 0, 0 };
+ //Xor(v, R);
+ x[0] ^= 0xe1000000;
+ }
+ }
+
+ internal static void MultiplyP8(uint[] x)
+ {
+// for (int i = 8; i != 0; --i)
+// {
+// MultiplyP(x);
+// }
+
+ uint lsw = x[3];
+ ShiftRightN(x, 8);
+ for (int i = 7; i >= 0; --i)
+ {
+ if ((lsw & (1 << i)) != 0)
+ {
+ x[0] ^= (0xe1000000 >> (7 - i));
+ }
+ }
+ }
+
+ internal static void ShiftRight(byte[] block)
+ {
+ int i = 0;
+ byte bit = 0;
+ for (;;)
+ {
+ byte b = block[i];
+ block[i] = (byte)((b >> 1) | bit);
+ if (++i == 16) break;
+ bit = (byte)(b << 7);
+ }
+ }
+
+ internal static void ShiftRight(uint[] block)
+ {
+ int i = 0;
+ uint bit = 0;
+ for (;;)
+ {
+ uint b = block[i];
+ block[i] = (b >> 1) | bit;
+ if (++i == 4) break;
+ bit = b << 31;
+ }
+ }
+
+ internal static void ShiftRightN(uint[] block, int n)
+ {
+ int i = 0;
+ uint bit = 0;
+ for (;;)
+ {
+ uint b = block[i];
+ block[i] = (b >> n) | bit;
+ if (++i == 4) break;
+ bit = b << (32 - n);
+ }
+ }
+
+ internal static void Xor(byte[] block, byte[] val)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+
+ internal static void Xor(uint[] block, uint[] val)
+ {
+ for (int i = 3; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs b/Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs
new file mode 100644
index 000000000..5b4ce9d7a
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+ public interface IGcmExponentiator
+ {
+ void Init(byte[] x);
+ void ExponentiateX(long pow, byte[] output);
+ }
+}
diff --git a/Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs
new file mode 100644
index 000000000..ec7b906ee
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+ public interface IGcmMultiplier
+ {
+ void Init(byte[] H);
+ void MultiplyH(byte[] x);
+ }
+}
diff --git a/Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs b/Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs
new file mode 100644
index 000000000..9425a3d9d
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs
@@ -0,0 +1,44 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+ public class Tables1kGcmExponentiator
+ : IGcmExponentiator
+ {
+ // A lookup table of the power-of-two powers of 'x'
+ private byte[][] lookupPowX2 = new byte[64][];
+
+ public void Init(byte[] x)
+ {
+ lookupPowX2[0] = GcmUtilities.OneAsBytes();
+ lookupPowX2[1] = Arrays.Clone(x);
+
+ for (int i = 2; i != 64; ++i)
+ {
+ byte[] tmp = Arrays.Clone(lookupPowX2[i - 1]);
+ GcmUtilities.Multiply(tmp, tmp);
+ lookupPowX2[i] = tmp;
+ }
+ }
+
+ public void ExponentiateX(long pow, byte[] output)
+ {
+ byte[] y = GcmUtilities.OneAsBytes();
+ int powX2 = 1;
+
+ while (pow > 0)
+ {
+ if ((pow & 1L) != 0)
+ {
+ GcmUtilities.Multiply(y, lookupPowX2[powX2]);
+ }
+ ++powX2;
+ pow >>= 1;
+ }
+
+ Array.Copy(y, 0, output, 0, 16);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs
new file mode 100644
index 000000000..f089dfe8d
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs
@@ -0,0 +1,64 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+ public class Tables64kGcmMultiplier
+ : IGcmMultiplier
+ {
+ private readonly uint[][][] M = new uint[16][][];
+
+ public void Init(byte[] H)
+ {
+ M[0] = new uint[256][];
+ M[0][0] = new uint[4];
+ M[0][128] = GcmUtilities.AsUints(H);
+ for (int j = 64; j >= 1; j >>= 1)
+ {
+ uint[] tmp = (uint[])M[0][j + j].Clone();
+ GcmUtilities.MultiplyP(tmp);
+ M[0][j] = tmp;
+ }
+ for (int i = 0;;)
+ {
+ for (int j = 2; j < 256; j += j)
+ {
+ for (int k = 1; k < j; ++k)
+ {
+ uint[] tmp = (uint[])M[i][j].Clone();
+ GcmUtilities.Xor(tmp, M[i][k]);
+ M[i][j + k] = tmp;
+ }
+ }
+
+ if (++i == 16) return;
+
+ M[i] = new uint[256][];
+ M[i][0] = new uint[4];
+ for (int j = 128; j > 0; j >>= 1)
+ {
+ uint[] tmp = (uint[])M[i - 1][j].Clone();
+ GcmUtilities.MultiplyP8(tmp);
+ M[i][j] = tmp;
+ }
+ }
+ }
+
+ public void MultiplyH(byte[] x)
+ {
+ uint[] z = new uint[4];
+ for (int i = 0; i != 16; ++i)
+ {
+ //GcmUtilities.Xor(z, M[i][x[i]]);
+ uint[] m = M[i][x[i]];
+ z[0] ^= m[0];
+ z[1] ^= m[1];
+ z[2] ^= m[2];
+ z[3] ^= m[3];
+ }
+
+ Pack.UInt32_To_BE(z, x, 0);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs
new file mode 100644
index 000000000..91d58fab8
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs
@@ -0,0 +1,90 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+ public class Tables8kGcmMultiplier
+ : IGcmMultiplier
+ {
+ private readonly uint[][][] M = new uint[32][][];
+
+ public void Init(byte[] H)
+ {
+ M[0] = new uint[16][];
+ M[1] = new uint[16][];
+ M[0][0] = new uint[4];
+ M[1][0] = new uint[4];
+ M[1][8] = GcmUtilities.AsUints(H);
+
+ for (int j = 4; j >= 1; j >>= 1)
+ {
+ uint[] tmp = (uint[])M[1][j + j].Clone();
+ GcmUtilities.MultiplyP(tmp);
+ M[1][j] = tmp;
+ }
+
+ {
+ uint[] tmp = (uint[])M[1][1].Clone();
+ GcmUtilities.MultiplyP(tmp);
+ M[0][8] = tmp;
+ }
+
+ for (int j = 4; j >= 1; j >>= 1)
+ {
+ uint[] tmp = (uint[])M[0][j + j].Clone();
+ GcmUtilities.MultiplyP(tmp);
+ M[0][j] = tmp;
+ }
+
+ for (int i = 0;;)
+ {
+ for (int j = 2; j < 16; j += j)
+ {
+ for (int k = 1; k < j; ++k)
+ {
+ uint[] tmp = (uint[])M[i][j].Clone();
+ GcmUtilities.Xor(tmp, M[i][k]);
+ M[i][j + k] = tmp;
+ }
+ }
+
+ if (++i == 32) return;
+
+ if (i > 1)
+ {
+ M[i] = new uint[16][];
+ M[i][0] = new uint[4];
+ for(int j = 8; j > 0; j >>= 1)
+ {
+ uint[] tmp = (uint[])M[i - 2][j].Clone();
+ GcmUtilities.MultiplyP8(tmp);
+ M[i][j] = tmp;
+ }
+ }
+ }
+ }
+
+ public void MultiplyH(byte[] x)
+ {
+ uint[] z = new uint[4];
+ for (int i = 15; i >= 0; --i)
+ {
+ //GcmUtilities.Xor(z, M[i + i][x[i] & 0x0f]);
+ uint[] m = M[i + i][x[i] & 0x0f];
+ z[0] ^= m[0];
+ z[1] ^= m[1];
+ z[2] ^= m[2];
+ z[3] ^= m[3];
+ //GcmUtilities.Xor(z, M[i + i + 1][(x[i] & 0xf0) >> 4]);
+ m = M[i + i + 1][(x[i] & 0xf0) >> 4];
+ z[0] ^= m[0];
+ z[1] ^= m[1];
+ z[2] ^= m[2];
+ z[3] ^= m[3];
+ }
+
+ Pack.UInt32_To_BE(z, x, 0);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/paddings/BlockCipherPadding.cs b/Crypto/src/crypto/paddings/BlockCipherPadding.cs
new file mode 100644
index 000000000..33a5f9f0f
--- /dev/null
+++ b/Crypto/src/crypto/paddings/BlockCipherPadding.cs
@@ -0,0 +1,43 @@
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+
+namespace Org.BouncyCastle.Crypto.Paddings
+{
+ /**
+ * Block cipher padders are expected to conform to this interface
+ */
+ public interface IBlockCipherPadding
+ {
+ /**
+ * Initialise the padder.
+ *
+ * @param param parameters, if any required.
+ */
+ void Init(SecureRandom random);
+ //throws ArgumentException;
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ string PaddingName { get; }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ int AddPadding(byte[] input, int inOff);
+
+ /**
+ * return the number of pad bytes present in the block.
+ * @exception InvalidCipherTextException if the padding is badly formed
+ * or invalid.
+ */
+ int PadCount(byte[] input);
+ //throws InvalidCipherTextException;
+ }
+
+}
diff --git a/Crypto/src/crypto/paddings/ISO10126d2Padding.cs b/Crypto/src/crypto/paddings/ISO10126d2Padding.cs
new file mode 100644
index 000000000..e132a62dd
--- /dev/null
+++ b/Crypto/src/crypto/paddings/ISO10126d2Padding.cs
@@ -0,0 +1,76 @@
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+
+namespace Org.BouncyCastle.Crypto.Paddings
+{
+
+ /**
+ * A padder that adds ISO10126-2 padding to a block.
+ */
+ public class ISO10126d2Padding: IBlockCipherPadding
+ {
+ private SecureRandom random;
+
+ /**
+ * Initialise the padder.
+ *
+ * @param random a SecureRandom if available.
+ */
+ public void Init(
+ SecureRandom random)
+ //throws ArgumentException
+ {
+ this.random = (random != null) ? random : new SecureRandom();
+ }
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ public string PaddingName
+ {
+ get { return "ISO10126-2"; }
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ public int AddPadding(
+ byte[] input,
+ int inOff)
+ {
+ byte code = (byte)(input.Length - inOff);
+
+ while (inOff < (input.Length - 1))
+ {
+ input[inOff] = (byte)random.NextInt();
+ inOff++;
+ }
+
+ input[inOff] = code;
+
+ return code;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int PadCount(byte[] input)
+ //throws InvalidCipherTextException
+ {
+ int count = input[input.Length - 1] & 0xff;
+
+ if (count > input.Length)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ return count;
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/paddings/ISO7816d4Padding.cs b/Crypto/src/crypto/paddings/ISO7816d4Padding.cs
new file mode 100644
index 000000000..016b25a81
--- /dev/null
+++ b/Crypto/src/crypto/paddings/ISO7816d4Padding.cs
@@ -0,0 +1,79 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Paddings
+{
+ /**
+ * A padder that adds the padding according to the scheme referenced in
+ * ISO 7814-4 - scheme 2 from ISO 9797-1. The first byte is 0x80, rest is 0x00
+ */
+ public class ISO7816d4Padding
+ : IBlockCipherPadding
+ {
+ /**
+ * Initialise the padder.
+ *
+ * @param random - a SecureRandom if available.
+ */
+ public void Init(
+ SecureRandom random)
+ {
+ // nothing to do.
+ }
+
+ /**
+ * Return the name of the algorithm the padder implements.
+ *
+ * @return the name of the algorithm the padder implements.
+ */
+ public string PaddingName
+ {
+ get { return "ISO7816-4"; }
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ public int AddPadding(
+ byte[] input,
+ int inOff)
+ {
+ int added = (input.Length - inOff);
+
+ input[inOff]= (byte) 0x80;
+ inOff ++;
+
+ while (inOff < input.Length)
+ {
+ input[inOff] = (byte) 0;
+ inOff++;
+ }
+
+ return added;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int PadCount(
+ byte[] input)
+ {
+ int count = input.Length - 1;
+
+ while (count > 0 && input[count] == 0)
+ {
+ count--;
+ }
+
+ if (input[count] != (byte)0x80)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ return input.Length - count;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs b/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
new file mode 100644
index 000000000..fb8a92ba3
--- /dev/null
+++ b/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
@@ -0,0 +1,288 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Paddings
+{
+ /**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion with padding. The PaddedBufferedBlockCipher
+ * outputs a block only when the buffer is full and more data is being added,
+ * or on a doFinal (unless the current block in the buffer is a pad block).
+ * The default padding mechanism used is the one outlined in Pkcs5/Pkcs7.
+ */
+ public class PaddedBufferedBlockCipher
+ : BufferedBlockCipher
+ {
+ private readonly IBlockCipherPadding padding;
+
+ /**
+ * Create a buffered block cipher with the desired padding.
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ * @param padding the padding type.
+ */
+ public PaddedBufferedBlockCipher(
+ IBlockCipher cipher,
+ IBlockCipherPadding padding)
+ {
+ this.cipher = cipher;
+ this.padding = padding;
+
+ buf = new byte[cipher.GetBlockSize()];
+ bufOff = 0;
+ }
+
+ /**
+ * Create a buffered block cipher Pkcs7 padding
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ */
+ public PaddedBufferedBlockCipher(
+ IBlockCipher cipher)
+ : this(cipher, new Pkcs7Padding()) { }
+
+ /**
+ * initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ public override void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+
+ SecureRandom initRandom = null;
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)parameters;
+ initRandom = p.Random;
+ parameters = p.Parameters;
+ }
+
+ Reset();
+ padding.Init(initRandom);
+ cipher.Init(forEncryption, parameters);
+ }
+
+ /**
+ * return the minimum size of the output buffer required for an update
+ * plus a doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public override int GetOutputSize(
+ int length)
+ {
+ int total = length + bufOff;
+ int leftOver = total % buf.Length;
+
+ if (leftOver == 0)
+ {
+ if (forEncryption)
+ {
+ return total + buf.Length;
+ }
+
+ return total;
+ }
+
+ return total - leftOver + buf.Length;
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public override int GetUpdateOutputSize(
+ int length)
+ {
+ int total = length + bufOff;
+ int leftOver = total % buf.Length;
+
+ if (leftOver == 0)
+ {
+ return total - buf.Length;
+ }
+
+ return total - leftOver;
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessByte(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ int resultLen = 0;
+
+ if (bufOff == buf.Length)
+ {
+ resultLen = cipher.ProcessBlock(buf, 0, output, outOff);
+ bufOff = 0;
+ }
+
+ buf[bufOff++] = input;
+
+ return resultLen;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ if (length < 0)
+ {
+ throw new ArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = GetBlockSize();
+ int outLength = GetUpdateOutputSize(length);
+
+ if (outLength > 0)
+ {
+ if ((outOff + outLength) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.Length - bufOff;
+
+ if (length > gapLen)
+ {
+ Array.Copy(input, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.ProcessBlock(buf, 0, output, outOff);
+
+ bufOff = 0;
+ length -= gapLen;
+ inOff += gapLen;
+
+ while (length > buf.Length)
+ {
+ resultLen += cipher.ProcessBlock(input, inOff, output, outOff + resultLen);
+
+ length -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ Array.Copy(input, inOff, buf, bufOff, length);
+
+ bufOff += length;
+
+ return resultLen;
+ }
+
+ /**
+ * Process the last block in the buffer. If the buffer is currently
+ * full and padding needs to be added a call to doFinal will produce
+ * 2 * GetBlockSize() bytes.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output or we are decrypting and the input is not block size aligned.
+ * @exception InvalidOperationException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if padding is expected and not found.
+ */
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ int blockSize = cipher.GetBlockSize();
+ int resultLen = 0;
+
+ if (forEncryption)
+ {
+ if (bufOff == blockSize)
+ {
+ if ((outOff + 2 * blockSize) > output.Length)
+ {
+ Reset();
+
+ throw new DataLengthException("output buffer too short");
+ }
+
+ resultLen = cipher.ProcessBlock(buf, 0, output, outOff);
+ bufOff = 0;
+ }
+
+ padding.AddPadding(buf, bufOff);
+
+ resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen);
+
+ Reset();
+ }
+ else
+ {
+ if (bufOff == blockSize)
+ {
+ resultLen = cipher.ProcessBlock(buf, 0, buf, 0);
+ bufOff = 0;
+ }
+ else
+ {
+ Reset();
+
+ throw new DataLengthException("last block incomplete in decryption");
+ }
+
+ try
+ {
+ resultLen -= padding.PadCount(buf);
+
+ Array.Copy(buf, 0, output, outOff, resultLen);
+ }
+ finally
+ {
+ Reset();
+ }
+ }
+
+ return resultLen;
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/paddings/Pkcs7Padding.cs b/Crypto/src/crypto/paddings/Pkcs7Padding.cs
new file mode 100644
index 000000000..f3166fd96
--- /dev/null
+++ b/Crypto/src/crypto/paddings/Pkcs7Padding.cs
@@ -0,0 +1,79 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Paddings
+{
+ /**
+ * A padder that adds Pkcs7/Pkcs5 padding to a block.
+ */
+ public class Pkcs7Padding
+ : IBlockCipherPadding
+ {
+ /**
+ * Initialise the padder.
+ *
+ * @param random - a SecureRandom if available.
+ */
+ public void Init(
+ SecureRandom random)
+ {
+ // nothing to do.
+ }
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ public string PaddingName
+ {
+ get { return "PKCS7"; }
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ public int AddPadding(
+ byte[] input,
+ int inOff)
+ {
+ byte code = (byte)(input.Length - inOff);
+
+ while (inOff < input.Length)
+ {
+ input[inOff] = code;
+ inOff++;
+ }
+
+ return code;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int PadCount(
+ byte[] input)
+ {
+ int count = (int) input[input.Length - 1];
+
+ if (count < 1 || count > input.Length)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ for (int i = 1; i <= count; i++)
+ {
+ if (input[input.Length - i] != count)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+ }
+
+ return count;
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/paddings/TbcPadding.cs b/Crypto/src/crypto/paddings/TbcPadding.cs
new file mode 100644
index 000000000..74b64e8e1
--- /dev/null
+++ b/Crypto/src/crypto/paddings/TbcPadding.cs
@@ -0,0 +1,79 @@
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Paddings
+{
+
+ /// <summary> A padder that adds Trailing-Bit-Compliment padding to a block.
+ /// <p>
+ /// This padding pads the block out compliment of the last bit
+ /// of the plain text.
+ /// </p>
+ /// </summary>
+ public class TbcPadding
+ : IBlockCipherPadding
+ {
+ /// <summary> Return the name of the algorithm the cipher implements.</summary>
+ /// <returns> the name of the algorithm the cipher implements.
+ /// </returns>
+ public string PaddingName
+ {
+ get { return "TBC"; }
+ }
+
+ /// <summary> Initialise the padder.</summary>
+ /// <param name="random">- a SecureRandom if available.
+ /// </param>
+ public virtual void Init(SecureRandom random)
+ {
+ // nothing to do.
+ }
+
+ /// <summary> add the pad bytes to the passed in block, returning the
+ /// number of bytes added.
+ /// <p>
+ /// Note: this assumes that the last block of plain text is always
+ /// passed to it inside in. i.e. if inOff is zero, indicating the
+ /// entire block is to be overwritten with padding the value of in
+ /// should be the same as the last block of plain text.
+ /// </p>
+ /// </summary>
+ public virtual int AddPadding(byte[] input, int inOff)
+ {
+ int count = input.Length - inOff;
+ byte code;
+
+ if (inOff > 0)
+ {
+ code = (byte)((input[inOff - 1] & 0x01) == 0?0xff:0x00);
+ }
+ else
+ {
+ code = (byte)((input[input.Length - 1] & 0x01) == 0?0xff:0x00);
+ }
+
+ while (inOff < input.Length)
+ {
+ input[inOff] = code;
+ inOff++;
+ }
+
+ return count;
+ }
+
+ /// <summary> return the number of pad bytes present in the block.</summary>
+ public virtual int PadCount(byte[] input)
+ {
+ byte code = input[input.Length - 1];
+
+ int index = input.Length - 1;
+ while (index > 0 && input[index - 1] == code)
+ {
+ index--;
+ }
+
+ return input.Length - index;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/paddings/X923Padding.cs b/Crypto/src/crypto/paddings/X923Padding.cs
new file mode 100644
index 000000000..cc1b52b3e
--- /dev/null
+++ b/Crypto/src/crypto/paddings/X923Padding.cs
@@ -0,0 +1,82 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Paddings
+{
+ /**
+ * A padder that adds X9.23 padding to a block - if a SecureRandom is
+ * passed in random padding is assumed, otherwise padding with zeros is used.
+ */
+ public class X923Padding
+ : IBlockCipherPadding
+ {
+ private SecureRandom random;
+
+ /**
+ * Initialise the padder.
+ *
+ * @param random a SecureRandom if one is available.
+ */
+ public void Init(
+ SecureRandom random)
+ {
+ this.random = random;
+ }
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ public string PaddingName
+ {
+ get { return "X9.23"; }
+ }
+
+ /**
+ * add the pad bytes to the passed in block, returning the
+ * number of bytes added.
+ */
+ public int AddPadding(
+ byte[] input,
+ int inOff)
+ {
+ byte code = (byte)(input.Length - inOff);
+
+ while (inOff < input.Length - 1)
+ {
+ if (random == null)
+ {
+ input[inOff] = 0;
+ }
+ else
+ {
+ input[inOff] = (byte)random.NextInt();
+ }
+ inOff++;
+ }
+
+ input[inOff] = code;
+
+ return code;
+ }
+
+ /**
+ * return the number of pad bytes present in the block.
+ */
+ public int PadCount(
+ byte[] input)
+ {
+ int count = input[input.Length - 1] & 0xff;
+
+ if (count > input.Length)
+ {
+ throw new InvalidCipherTextException("pad block corrupted");
+ }
+
+ return count;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/paddings/ZeroBytePadding.cs b/Crypto/src/crypto/paddings/ZeroBytePadding.cs
new file mode 100644
index 000000000..0d55ca4c2
--- /dev/null
+++ b/Crypto/src/crypto/paddings/ZeroBytePadding.cs
@@ -0,0 +1,68 @@
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Paddings
+{
+
+ /// <summary> A padder that adds Null byte padding to a block.</summary>
+ public class ZeroBytePadding : IBlockCipherPadding
+ {
+ /// <summary> Return the name of the algorithm the cipher implements.
+ ///
+ /// </summary>
+ /// <returns> the name of the algorithm the cipher implements.
+ /// </returns>
+ public string PaddingName
+ {
+ get { return "ZeroBytePadding"; }
+ }
+
+ /// <summary> Initialise the padder.
+ ///
+ /// </summary>
+ /// <param name="random">- a SecureRandom if available.
+ /// </param>
+ public void Init(SecureRandom random)
+ {
+ // nothing to do.
+ }
+
+ /// <summary> add the pad bytes to the passed in block, returning the
+ /// number of bytes added.
+ /// </summary>
+ public int AddPadding(
+ byte[] input,
+ int inOff)
+ {
+ int added = (input.Length - inOff);
+
+ while (inOff < input.Length)
+ {
+ input[inOff] = (byte) 0;
+ inOff++;
+ }
+
+ return added;
+ }
+
+ /// <summary> return the number of pad bytes present in the block.</summary>
+ public int PadCount(
+ byte[] input)
+ {
+ int count = input.Length;
+
+ while (count > 0)
+ {
+ if (input[count - 1] != 0)
+ {
+ break;
+ }
+
+ count--;
+ }
+
+ return input.Length - count;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/AEADParameters.cs b/Crypto/src/crypto/parameters/AEADParameters.cs
new file mode 100644
index 000000000..06b2f5c38
--- /dev/null
+++ b/Crypto/src/crypto/parameters/AEADParameters.cs
@@ -0,0 +1,53 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class AeadParameters
+ : ICipherParameters
+ {
+ private readonly byte[] associatedText;
+ private readonly byte[] nonce;
+ private readonly KeyParameter key;
+ private readonly int macSize;
+
+ /**
+ * Base constructor.
+ *
+ * @param key key to be used by underlying cipher
+ * @param macSize macSize in bits
+ * @param nonce nonce to be used
+ * @param associatedText associated text, if any
+ */
+ public AeadParameters(
+ KeyParameter key,
+ int macSize,
+ byte[] nonce,
+ byte[] associatedText)
+ {
+ this.key = key;
+ this.nonce = nonce;
+ this.macSize = macSize;
+ this.associatedText = associatedText;
+ }
+
+ public virtual KeyParameter Key
+ {
+ get { return key; }
+ }
+
+ public virtual int MacSize
+ {
+ get { return macSize; }
+ }
+
+ public virtual byte[] GetAssociatedText()
+ {
+ return associatedText;
+ }
+
+ public virtual byte[] GetNonce()
+ {
+ return nonce;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/CcmParameters.cs b/Crypto/src/crypto/parameters/CcmParameters.cs
new file mode 100644
index 000000000..8dc981e1f
--- /dev/null
+++ b/Crypto/src/crypto/parameters/CcmParameters.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class CcmParameters
+ : AeadParameters
+ {
+ /**
+ * Base constructor.
+ *
+ * @param key key to be used by underlying cipher
+ * @param macSize macSize in bits
+ * @param nonce nonce to be used
+ * @param associatedText associated text, if any
+ */
+ public CcmParameters(
+ KeyParameter key,
+ int macSize,
+ byte[] nonce,
+ byte[] associatedText)
+ : base(key, macSize, nonce, associatedText)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DHKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/DHKeyGenerationParameters.cs
new file mode 100644
index 000000000..ab3e18f09
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DHKeyGenerationParameters.cs
@@ -0,0 +1,31 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DHKeyGenerationParameters
+ : KeyGenerationParameters
+ {
+ private readonly DHParameters parameters;
+
+ public DHKeyGenerationParameters(
+ SecureRandom random,
+ DHParameters parameters)
+ : base(random, GetStrength(parameters))
+ {
+ this.parameters = parameters;
+ }
+
+ public DHParameters Parameters
+ {
+ get { return parameters; }
+ }
+
+ internal static int GetStrength(
+ DHParameters parameters)
+ {
+ return parameters.L != 0 ? parameters.L : parameters.P.BitLength;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DHKeyParameters.cs b/Crypto/src/crypto/parameters/DHKeyParameters.cs
new file mode 100644
index 000000000..1a5c1386f
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DHKeyParameters.cs
@@ -0,0 +1,76 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DHKeyParameters
+ : AsymmetricKeyParameter
+ {
+ private readonly DHParameters parameters;
+ private readonly DerObjectIdentifier algorithmOid;
+
+ protected DHKeyParameters(
+ bool isPrivate,
+ DHParameters parameters)
+ : this(isPrivate, parameters, PkcsObjectIdentifiers.DhKeyAgreement)
+ {
+ }
+
+ protected DHKeyParameters(
+ bool isPrivate,
+ DHParameters parameters,
+ DerObjectIdentifier algorithmOid)
+ : base(isPrivate)
+ {
+ // TODO Should we allow parameters to be null?
+ this.parameters = parameters;
+ this.algorithmOid = algorithmOid;
+ }
+
+ public DHParameters Parameters
+ {
+ get { return parameters; }
+ }
+
+ public DerObjectIdentifier AlgorithmOid
+ {
+ get { return algorithmOid; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DHKeyParameters other = obj as DHKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DHKeyParameters other)
+ {
+ return Platform.Equals(parameters, other.parameters)
+ && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ int hc = base.GetHashCode();
+
+ if (parameters != null)
+ {
+ hc ^= parameters.GetHashCode();
+ }
+
+ return hc;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DHParameters.cs b/Crypto/src/crypto/parameters/DHParameters.cs
new file mode 100644
index 000000000..a0544e73b
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DHParameters.cs
@@ -0,0 +1,184 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DHParameters
+ : ICipherParameters
+ {
+ private const int DefaultMinimumLength = 160;
+
+ private readonly BigInteger p, g, q, j;
+ private readonly int m, l;
+ private readonly DHValidationParameters validation;
+
+ private static int GetDefaultMParam(
+ int lParam)
+ {
+ if (lParam == 0)
+ return DefaultMinimumLength;
+
+ return System.Math.Min(lParam, DefaultMinimumLength);
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g)
+ : this(p, g, null, 0)
+ {
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q)
+ : this(p, g, q, 0)
+ {
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ int l)
+ : this(p, g, q, GetDefaultMParam(l), l, null, null)
+ {
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ int m,
+ int l)
+ : this(p, g, q, m, l, null, null)
+ {
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ BigInteger j,
+ DHValidationParameters validation)
+ : this(p, g, q, DefaultMinimumLength, 0, j, validation)
+ {
+ }
+
+ public DHParameters(
+ BigInteger p,
+ BigInteger g,
+ BigInteger q,
+ int m,
+ int l,
+ BigInteger j,
+ DHValidationParameters validation)
+ {
+ if (p == null)
+ throw new ArgumentNullException("p");
+ if (g == null)
+ throw new ArgumentNullException("g");
+ if (!p.TestBit(0))
+ throw new ArgumentException("field must be an odd prime", "p");
+ if (g.CompareTo(BigInteger.Two) < 0
+ || g.CompareTo(p.Subtract(BigInteger.Two)) > 0)
+ throw new ArgumentException("generator must in the range [2, p - 2]", "g");
+ if (q != null && q.BitLength >= p.BitLength)
+ throw new ArgumentException("q too big to be a factor of (p-1)", "q");
+ if (m >= p.BitLength)
+ throw new ArgumentException("m value must be < bitlength of p", "m");
+ if (l != 0)
+ {
+ if (l >= p.BitLength)
+ throw new ArgumentException("when l value specified, it must be less than bitlength(p)", "l");
+ if (l < m)
+ throw new ArgumentException("when l value specified, it may not be less than m value", "l");
+ }
+ if (j != null && j.CompareTo(BigInteger.Two) < 0)
+ throw new ArgumentException("subgroup factor must be >= 2", "j");
+
+ // TODO If q, j both provided, validate p = jq + 1 ?
+
+ this.p = p;
+ this.g = g;
+ this.q = q;
+ this.m = m;
+ this.l = l;
+ this.j = j;
+ this.validation = validation;
+ }
+
+ public BigInteger P
+ {
+ get { return p; }
+ }
+
+ public BigInteger G
+ {
+ get { return g; }
+ }
+
+ public BigInteger Q
+ {
+ get { return q; }
+ }
+
+ public BigInteger J
+ {
+ get { return j; }
+ }
+
+ /// <summary>The minimum bitlength of the private value.</summary>
+ public int M
+ {
+ get { return m; }
+ }
+
+ /// <summary>The bitlength of the private value.</summary>
+ public int L
+ {
+ get { return l; }
+ }
+
+ public DHValidationParameters ValidationParameters
+ {
+ get { return validation; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DHParameters other = obj as DHParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DHParameters other)
+ {
+ return p.Equals(other.p)
+ && g.Equals(other.g)
+ && Platform.Equals(q, other.q);
+ }
+
+ public override int GetHashCode()
+ {
+ int hc = p.GetHashCode() ^ g.GetHashCode();
+
+ if (q != null)
+ {
+ hc ^= q.GetHashCode();
+ }
+
+ return hc;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DHPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/DHPrivateKeyParameters.cs
new file mode 100644
index 000000000..fc724df81
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DHPrivateKeyParameters.cs
@@ -0,0 +1,60 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DHPrivateKeyParameters
+ : DHKeyParameters
+ {
+ private readonly BigInteger x;
+
+ public DHPrivateKeyParameters(
+ BigInteger x,
+ DHParameters parameters)
+ : base(true, parameters)
+ {
+ this.x = x;
+ }
+
+ public DHPrivateKeyParameters(
+ BigInteger x,
+ DHParameters parameters,
+ DerObjectIdentifier algorithmOid)
+ : base(true, parameters, algorithmOid)
+ {
+ this.x = x;
+ }
+
+ public BigInteger X
+ {
+ get { return x; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DHPrivateKeyParameters other = obj as DHPrivateKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DHPrivateKeyParameters other)
+ {
+ return x.Equals(other.x) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return x.GetHashCode() ^ base.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DHPublicKeyParameters.cs b/Crypto/src/crypto/parameters/DHPublicKeyParameters.cs
new file mode 100644
index 000000000..e79375f71
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DHPublicKeyParameters.cs
@@ -0,0 +1,66 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DHPublicKeyParameters
+ : DHKeyParameters
+ {
+ private readonly BigInteger y;
+
+ public DHPublicKeyParameters(
+ BigInteger y,
+ DHParameters parameters)
+ : base(false, parameters)
+ {
+ if (y == null)
+ throw new ArgumentNullException("y");
+
+ this.y = y;
+ }
+
+ public DHPublicKeyParameters(
+ BigInteger y,
+ DHParameters parameters,
+ DerObjectIdentifier algorithmOid)
+ : base(false, parameters, algorithmOid)
+ {
+ if (y == null)
+ throw new ArgumentNullException("y");
+
+ this.y = y;
+ }
+
+ public BigInteger Y
+ {
+ get { return y; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DHPublicKeyParameters other = obj as DHPublicKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DHPublicKeyParameters other)
+ {
+ return y.Equals(other.y) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return y.GetHashCode() ^ base.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DHValidationParameters.cs b/Crypto/src/crypto/parameters/DHValidationParameters.cs
new file mode 100644
index 000000000..50c0739fa
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DHValidationParameters.cs
@@ -0,0 +1,59 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DHValidationParameters
+ {
+ private readonly byte[] seed;
+ private readonly int counter;
+
+ public DHValidationParameters(
+ byte[] seed,
+ int counter)
+ {
+ if (seed == null)
+ throw new ArgumentNullException("seed");
+
+ this.seed = (byte[]) seed.Clone();
+ this.counter = counter;
+ }
+
+ public byte[] GetSeed()
+ {
+ return (byte[]) seed.Clone();
+ }
+
+ public int Counter
+ {
+ get { return counter; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DHValidationParameters other = obj as DHValidationParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DHValidationParameters other)
+ {
+ return counter == other.counter
+ && Arrays.AreEqual(this.seed, other.seed);
+ }
+
+ public override int GetHashCode()
+ {
+ return counter.GetHashCode() ^ Arrays.GetHashCode(seed);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DesEdeParameters.cs b/Crypto/src/crypto/parameters/DesEdeParameters.cs
new file mode 100644
index 000000000..420aaecea
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DesEdeParameters.cs
@@ -0,0 +1,95 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DesEdeParameters
+ : DesParameters
+ {
+ /*
+ * DES-EDE Key length in bytes.
+ */
+ public const int DesEdeKeyLength = 24;
+
+ private static byte[] FixKey(
+ byte[] key,
+ int keyOff,
+ int keyLen)
+ {
+ byte[] tmp = new byte[24];
+
+ switch (keyLen)
+ {
+ case 16:
+ Array.Copy(key, keyOff, tmp, 0, 16);
+ Array.Copy(key, keyOff, tmp, 16, 8);
+ break;
+ case 24:
+ Array.Copy(key, keyOff, tmp, 0, 24);
+ break;
+ default:
+ throw new ArgumentException("Bad length for DESede key: " + keyLen, "keyLen");
+ }
+
+ if (IsWeakKey(tmp))
+ throw new ArgumentException("attempt to create weak DESede key");
+
+ return tmp;
+ }
+
+ public DesEdeParameters(
+ byte[] key)
+ : base(FixKey(key, 0, key.Length))
+ {
+ }
+
+ public DesEdeParameters(
+ byte[] key,
+ int keyOff,
+ int keyLen)
+ : base(FixKey(key, keyOff, keyLen))
+ {
+ }
+
+ /**
+ * return true if the passed in key is a DES-EDE weak key.
+ *
+ * @param key bytes making up the key
+ * @param offset offset into the byte array the key starts at
+ * @param length number of bytes making up the key
+ */
+ public static bool IsWeakKey(
+ byte[] key,
+ int offset,
+ int length)
+ {
+ for (int i = offset; i < length; i += DesKeyLength)
+ {
+ if (DesParameters.IsWeakKey(key, i))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * return true if the passed in key is a DES-EDE weak key.
+ *
+ * @param key bytes making up the key
+ * @param offset offset into the byte array the key starts at
+ */
+ public static new bool IsWeakKey(
+ byte[] key,
+ int offset)
+ {
+ return IsWeakKey(key, offset, key.Length - offset);
+ }
+
+ public static new bool IsWeakKey(
+ byte[] key)
+ {
+ return IsWeakKey(key, 0, key.Length);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DesParameters.cs b/Crypto/src/crypto/parameters/DesParameters.cs
new file mode 100644
index 000000000..ee37cd861
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DesParameters.cs
@@ -0,0 +1,130 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DesParameters
+ : KeyParameter
+ {
+ public DesParameters(
+ byte[] key)
+ : base(key)
+ {
+ if (IsWeakKey(key))
+ throw new ArgumentException("attempt to create weak DES key");
+ }
+
+ public DesParameters(
+ byte[] key,
+ int keyOff,
+ int keyLen)
+ : base(key, keyOff, keyLen)
+ {
+ if (IsWeakKey(key, keyOff))
+ throw new ArgumentException("attempt to create weak DES key");
+ }
+
+ /*
+ * DES Key Length in bytes.
+ */
+ public const int DesKeyLength = 8;
+
+ /*
+ * Table of weak and semi-weak keys taken from Schneier pp281
+ */
+ private const int N_DES_WEAK_KEYS = 16;
+
+ private static readonly byte[] DES_weak_keys =
+ {
+ /* weak keys */
+ (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01, (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,
+ (byte)0x1f,(byte)0x1f,(byte)0x1f,(byte)0x1f, (byte)0x0e,(byte)0x0e,(byte)0x0e,(byte)0x0e,
+ (byte)0xe0,(byte)0xe0,(byte)0xe0,(byte)0xe0, (byte)0xf1,(byte)0xf1,(byte)0xf1,(byte)0xf1,
+ (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe, (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe,
+
+ /* semi-weak keys */
+ (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe, (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe,
+ (byte)0x1f,(byte)0xe0,(byte)0x1f,(byte)0xe0, (byte)0x0e,(byte)0xf1,(byte)0x0e,(byte)0xf1,
+ (byte)0x01,(byte)0xe0,(byte)0x01,(byte)0xe0, (byte)0x01,(byte)0xf1,(byte)0x01,(byte)0xf1,
+ (byte)0x1f,(byte)0xfe,(byte)0x1f,(byte)0xfe, (byte)0x0e,(byte)0xfe,(byte)0x0e,(byte)0xfe,
+ (byte)0x01,(byte)0x1f,(byte)0x01,(byte)0x1f, (byte)0x01,(byte)0x0e,(byte)0x01,(byte)0x0e,
+ (byte)0xe0,(byte)0xfe,(byte)0xe0,(byte)0xfe, (byte)0xf1,(byte)0xfe,(byte)0xf1,(byte)0xfe,
+ (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01, (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01,
+ (byte)0xe0,(byte)0x1f,(byte)0xe0,(byte)0x1f, (byte)0xf1,(byte)0x0e,(byte)0xf1,(byte)0x0e,
+ (byte)0xe0,(byte)0x01,(byte)0xe0,(byte)0x01, (byte)0xf1,(byte)0x01,(byte)0xf1,(byte)0x01,
+ (byte)0xfe,(byte)0x1f,(byte)0xfe,(byte)0x1f, (byte)0xfe,(byte)0x0e,(byte)0xfe,(byte)0x0e,
+ (byte)0x1f,(byte)0x01,(byte)0x1f,(byte)0x01, (byte)0x0e,(byte)0x01,(byte)0x0e,(byte)0x01,
+ (byte)0xfe,(byte)0xe0,(byte)0xfe,(byte)0xe0, (byte)0xfe,(byte)0xf1,(byte)0xfe,(byte)0xf1
+ };
+
+ /**
+ * DES has 16 weak keys. This method will check
+ * if the given DES key material is weak or semi-weak.
+ * Key material that is too short is regarded as weak.
+ * <p>
+ * See <a href="http://www.counterpane.com/applied.html">"Applied
+ * Cryptography"</a> by Bruce Schneier for more information.
+ * </p>
+ * @return true if the given DES key material is weak or semi-weak,
+ * false otherwise.
+ */
+ public static bool IsWeakKey(
+ byte[] key,
+ int offset)
+ {
+ if (key.Length - offset < DesKeyLength)
+ throw new ArgumentException("key material too short.");
+
+ //nextkey:
+ for (int i = 0; i < N_DES_WEAK_KEYS; i++)
+ {
+ bool unmatch = false;
+ for (int j = 0; j < DesKeyLength; j++)
+ {
+ if (key[j + offset] != DES_weak_keys[i * DesKeyLength + j])
+ {
+ //continue nextkey;
+ unmatch = true;
+ break;
+ }
+ }
+
+ if (!unmatch)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static bool IsWeakKey(
+ byte[] key)
+ {
+ return IsWeakKey(key, 0);
+ }
+
+ /**
+ * DES Keys use the LSB as the odd parity bit. This can
+ * be used to check for corrupt keys.
+ *
+ * @param bytes the byte array to set the parity on.
+ */
+ public static void SetOddParity(
+ byte[] bytes)
+ {
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ int b = bytes[i];
+ bytes[i] = (byte)((b & 0xfe) |
+ ((((b >> 1) ^
+ (b >> 2) ^
+ (b >> 3) ^
+ (b >> 4) ^
+ (b >> 5) ^
+ (b >> 6) ^
+ (b >> 7)) ^ 0x01) & 0x01));
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/parameters/DsaKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/DsaKeyGenerationParameters.cs
new file mode 100644
index 000000000..86d6f5bd4
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DsaKeyGenerationParameters.cs
@@ -0,0 +1,26 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DsaKeyGenerationParameters
+ : KeyGenerationParameters
+ {
+ private readonly DsaParameters parameters;
+
+ public DsaKeyGenerationParameters(
+ SecureRandom random,
+ DsaParameters parameters)
+ : base(random, parameters.P.BitLength - 1)
+ {
+ this.parameters = parameters;
+ }
+
+ public DsaParameters Parameters
+ {
+ get { return parameters; }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/parameters/DsaKeyParameters.cs b/Crypto/src/crypto/parameters/DsaKeyParameters.cs
new file mode 100644
index 000000000..5fe6d7ab4
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DsaKeyParameters.cs
@@ -0,0 +1,59 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public abstract class DsaKeyParameters
+ : AsymmetricKeyParameter
+ {
+ private readonly DsaParameters parameters;
+
+ protected DsaKeyParameters(
+ bool isPrivate,
+ DsaParameters parameters)
+ : base(isPrivate)
+ {
+ // Note: parameters may be null
+ this.parameters = parameters;
+ }
+
+ public DsaParameters Parameters
+ {
+ get { return parameters; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DsaKeyParameters other = obj as DsaKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DsaKeyParameters other)
+ {
+ return Platform.Equals(parameters, other.parameters)
+ && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ int hc = base.GetHashCode();
+
+ if (parameters != null)
+ {
+ hc ^= parameters.GetHashCode();
+ }
+
+ return hc;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DsaParameters.cs b/Crypto/src/crypto/parameters/DsaParameters.cs
new file mode 100644
index 000000000..50d080ee2
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DsaParameters.cs
@@ -0,0 +1,85 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DsaParameters
+ : ICipherParameters
+ {
+ private readonly BigInteger p, q , g;
+ private readonly DsaValidationParameters validation;
+
+ public DsaParameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g)
+ : this(p, q, g, null)
+ {
+ }
+
+ public DsaParameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g,
+ DsaValidationParameters parameters)
+ {
+ if (p == null)
+ throw new ArgumentNullException("p");
+ if (q == null)
+ throw new ArgumentNullException("q");
+ if (g == null)
+ throw new ArgumentNullException("g");
+
+ this.p = p;
+ this.q = q;
+ this.g = g;
+ this.validation = parameters;
+ }
+
+ public BigInteger P
+ {
+ get { return p; }
+ }
+
+ public BigInteger Q
+ {
+ get { return q; }
+ }
+
+ public BigInteger G
+ {
+ get { return g; }
+ }
+
+ public DsaValidationParameters ValidationParameters
+ {
+ get { return validation; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DsaParameters other = obj as DsaParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DsaParameters other)
+ {
+ return p.Equals(other.p) && q.Equals(other.q) && g.Equals(other.g);
+ }
+
+ public override int GetHashCode()
+ {
+ return p.GetHashCode() ^ q.GetHashCode() ^ g.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DsaPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/DsaPrivateKeyParameters.cs
new file mode 100644
index 000000000..2abdd0e4f
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DsaPrivateKeyParameters.cs
@@ -0,0 +1,53 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DsaPrivateKeyParameters
+ : DsaKeyParameters
+ {
+ private readonly BigInteger x;
+
+ public DsaPrivateKeyParameters(
+ BigInteger x,
+ DsaParameters parameters)
+ : base(true, parameters)
+ {
+ if (x == null)
+ throw new ArgumentNullException("x");
+
+ this.x = x;
+ }
+
+ public BigInteger X
+ {
+ get { return x; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DsaPrivateKeyParameters other = obj as DsaPrivateKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DsaPrivateKeyParameters other)
+ {
+ return x.Equals(other.x) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return x.GetHashCode() ^ base.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DsaPublicKeyParameters.cs b/Crypto/src/crypto/parameters/DsaPublicKeyParameters.cs
new file mode 100644
index 000000000..f11f858f3
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DsaPublicKeyParameters.cs
@@ -0,0 +1,52 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DsaPublicKeyParameters
+ : DsaKeyParameters
+ {
+ private readonly BigInteger y;
+
+ public DsaPublicKeyParameters(
+ BigInteger y,
+ DsaParameters parameters)
+ : base(false, parameters)
+ {
+ if (y == null)
+ throw new ArgumentNullException("y");
+
+ this.y = y;
+ }
+
+ public BigInteger Y
+ {
+ get { return y; }
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DsaPublicKeyParameters other = obj as DsaPublicKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DsaPublicKeyParameters other)
+ {
+ return y.Equals(other.y) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return y.GetHashCode() ^ base.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/DsaValidationParameters.cs b/Crypto/src/crypto/parameters/DsaValidationParameters.cs
new file mode 100644
index 000000000..b9cdc4a79
--- /dev/null
+++ b/Crypto/src/crypto/parameters/DsaValidationParameters.cs
@@ -0,0 +1,59 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class DsaValidationParameters
+ {
+ private readonly byte[] seed;
+ private readonly int counter;
+
+ public DsaValidationParameters(
+ byte[] seed,
+ int counter)
+ {
+ if (seed == null)
+ throw new ArgumentNullException("seed");
+
+ this.seed = (byte[]) seed.Clone();
+ this.counter = counter;
+ }
+
+ public byte[] GetSeed()
+ {
+ return (byte[]) seed.Clone();
+ }
+
+ public int Counter
+ {
+ get { return counter; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ DsaValidationParameters other = obj as DsaValidationParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ DsaValidationParameters other)
+ {
+ return counter == other.counter
+ && Arrays.AreEqual(seed, other.seed);
+ }
+
+ public override int GetHashCode()
+ {
+ return counter.GetHashCode() ^ Arrays.GetHashCode(seed);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ECDomainParameters.cs b/Crypto/src/crypto/parameters/ECDomainParameters.cs
new file mode 100644
index 000000000..c6a3e4e72
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ECDomainParameters.cs
@@ -0,0 +1,116 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ECDomainParameters
+ {
+ internal ECCurve curve;
+ internal byte[] seed;
+ internal ECPoint g;
+ internal BigInteger n;
+ internal BigInteger h;
+
+ public ECDomainParameters(
+ ECCurve curve,
+ ECPoint g,
+ BigInteger n)
+ : this(curve, g, n, BigInteger.One)
+ {
+ }
+
+ public ECDomainParameters(
+ ECCurve curve,
+ ECPoint g,
+ BigInteger n,
+ BigInteger h)
+ : this(curve, g, n, h, null)
+ {
+ }
+
+ public ECDomainParameters(
+ ECCurve curve,
+ ECPoint g,
+ BigInteger n,
+ BigInteger h,
+ byte[] seed)
+ {
+ if (curve == null)
+ throw new ArgumentNullException("curve");
+ if (g == null)
+ throw new ArgumentNullException("g");
+ if (n == null)
+ throw new ArgumentNullException("n");
+ if (h == null)
+ throw new ArgumentNullException("h");
+
+ this.curve = curve;
+ this.g = g;
+ this.n = n;
+ this.h = h;
+ this.seed = Arrays.Clone(seed);
+ }
+
+ public ECCurve Curve
+ {
+ get { return curve; }
+ }
+
+ public ECPoint G
+ {
+ get { return g; }
+ }
+
+ public BigInteger N
+ {
+ get { return n; }
+ }
+
+ public BigInteger H
+ {
+ get { return h; }
+ }
+
+ public byte[] GetSeed()
+ {
+ return Arrays.Clone(seed);
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ ECDomainParameters other = obj as ECDomainParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ ECDomainParameters other)
+ {
+ return curve.Equals(other.curve)
+ && g.Equals(other.g)
+ && n.Equals(other.n)
+ && h.Equals(other.h)
+ && Arrays.AreEqual(seed, other.seed);
+ }
+
+ public override int GetHashCode()
+ {
+ return curve.GetHashCode()
+ ^ g.GetHashCode()
+ ^ n.GetHashCode()
+ ^ h.GetHashCode()
+ ^ Arrays.GetHashCode(seed);
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/parameters/ECKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/ECKeyGenerationParameters.cs
new file mode 100644
index 000000000..9b2b98845
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ECKeyGenerationParameters.cs
@@ -0,0 +1,41 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ECKeyGenerationParameters
+ : KeyGenerationParameters
+ {
+ private readonly ECDomainParameters domainParams;
+ private readonly DerObjectIdentifier publicKeyParamSet;
+
+ public ECKeyGenerationParameters(
+ ECDomainParameters domainParameters,
+ SecureRandom random)
+ : base(random, domainParameters.N.BitLength)
+ {
+ this.domainParams = domainParameters;
+ }
+
+ public ECKeyGenerationParameters(
+ DerObjectIdentifier publicKeyParamSet,
+ SecureRandom random)
+ : this(ECKeyParameters.LookupParameters(publicKeyParamSet), random)
+ {
+ this.publicKeyParamSet = publicKeyParamSet;
+ }
+
+ public ECDomainParameters DomainParameters
+ {
+ get { return domainParams; }
+ }
+
+ public DerObjectIdentifier PublicKeyParamSet
+ {
+ get { return publicKeyParamSet; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ECKeyParameters.cs b/Crypto/src/crypto/parameters/ECKeyParameters.cs
new file mode 100644
index 000000000..4d4622ced
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ECKeyParameters.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Globalization;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public abstract class ECKeyParameters
+ : AsymmetricKeyParameter
+ {
+ private readonly string algorithm;
+ private readonly ECDomainParameters parameters;
+ private readonly DerObjectIdentifier publicKeyParamSet;
+
+ protected ECKeyParameters(
+ string algorithm,
+ bool isPrivate,
+ ECDomainParameters parameters)
+ : base(isPrivate)
+ {
+ if (algorithm == null)
+ throw new ArgumentNullException("algorithm");
+ if (parameters == null)
+ throw new ArgumentNullException("parameters");
+
+ this.algorithm = VerifyAlgorithmName(algorithm);
+ this.parameters = parameters;
+ }
+
+ protected ECKeyParameters(
+ string algorithm,
+ bool isPrivate,
+ DerObjectIdentifier publicKeyParamSet)
+ : base(isPrivate)
+ {
+ if (algorithm == null)
+ throw new ArgumentNullException("algorithm");
+ if (publicKeyParamSet == null)
+ throw new ArgumentNullException("publicKeyParamSet");
+
+ this.algorithm = VerifyAlgorithmName(algorithm);
+ this.parameters = LookupParameters(publicKeyParamSet);
+ this.publicKeyParamSet = publicKeyParamSet;
+ }
+
+ public string AlgorithmName
+ {
+ get { return algorithm; }
+ }
+
+ public ECDomainParameters Parameters
+ {
+ get { return parameters; }
+ }
+
+ public DerObjectIdentifier PublicKeyParamSet
+ {
+ get { return publicKeyParamSet; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ ECDomainParameters other = obj as ECDomainParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ ECKeyParameters other)
+ {
+ return parameters.Equals(other.parameters) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return parameters.GetHashCode() ^ base.GetHashCode();
+ }
+
+ internal ECKeyGenerationParameters CreateKeyGenerationParameters(
+ SecureRandom random)
+ {
+ if (publicKeyParamSet != null)
+ {
+ return new ECKeyGenerationParameters(publicKeyParamSet, random);
+ }
+
+ return new ECKeyGenerationParameters(parameters, random);
+ }
+
+ private string VerifyAlgorithmName(
+ string algorithm)
+ {
+ string upper = algorithm.ToUpperInvariant();
+
+ switch (upper)
+ {
+ case "EC":
+ case "ECDSA":
+ case "ECDH":
+ case "ECDHC":
+ case "ECGOST3410":
+ case "ECMQV":
+ break;
+ default:
+ throw new ArgumentException("unrecognised algorithm: " + algorithm, "algorithm");
+ }
+
+ return upper;
+ }
+
+ internal static ECDomainParameters LookupParameters(
+ DerObjectIdentifier publicKeyParamSet)
+ {
+ if (publicKeyParamSet == null)
+ throw new ArgumentNullException("publicKeyParamSet");
+
+ ECDomainParameters p = ECGost3410NamedCurves.GetByOid(publicKeyParamSet);
+
+ if (p == null)
+ {
+ X9ECParameters x9 = ECKeyPairGenerator.FindECCurveByOid(publicKeyParamSet);
+
+ if (x9 == null)
+ {
+ throw new ArgumentException("OID is not a valid public key parameter set", "publicKeyParamSet");
+ }
+
+ p = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed());
+ }
+
+ return p;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ECPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/ECPrivateKeyParameters.cs
new file mode 100644
index 000000000..e6352d5d1
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ECPrivateKeyParameters.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Globalization;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ECPrivateKeyParameters
+ : ECKeyParameters
+ {
+ private readonly BigInteger d;
+
+ public ECPrivateKeyParameters(
+ BigInteger d,
+ ECDomainParameters parameters)
+ : this("EC", d, parameters)
+ {
+ }
+
+ [Obsolete("Use version with explicit 'algorithm' parameter")]
+ public ECPrivateKeyParameters(
+ BigInteger d,
+ DerObjectIdentifier publicKeyParamSet)
+ : base("ECGOST3410", true, publicKeyParamSet)
+ {
+ if (d == null)
+ throw new ArgumentNullException("d");
+
+ this.d = d;
+ }
+
+ public ECPrivateKeyParameters(
+ string algorithm,
+ BigInteger d,
+ ECDomainParameters parameters)
+ : base(algorithm, true, parameters)
+ {
+ if (d == null)
+ throw new ArgumentNullException("d");
+
+ this.d = d;
+ }
+
+ public ECPrivateKeyParameters(
+ string algorithm,
+ BigInteger d,
+ DerObjectIdentifier publicKeyParamSet)
+ : base(algorithm, true, publicKeyParamSet)
+ {
+ if (d == null)
+ throw new ArgumentNullException("d");
+
+ this.d = d;
+ }
+
+ public BigInteger D
+ {
+ get { return d; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ ECPrivateKeyParameters other = obj as ECPrivateKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ ECPrivateKeyParameters other)
+ {
+ return d.Equals(other.d) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return d.GetHashCode() ^ base.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ECPublicKeyParameters.cs b/Crypto/src/crypto/parameters/ECPublicKeyParameters.cs
new file mode 100644
index 000000000..9e71c2a25
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ECPublicKeyParameters.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Globalization;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ECPublicKeyParameters
+ : ECKeyParameters
+ {
+ private readonly ECPoint q;
+
+ public ECPublicKeyParameters(
+ ECPoint q,
+ ECDomainParameters parameters)
+ : this("EC", q, parameters)
+ {
+ }
+
+ [Obsolete("Use version with explicit 'algorithm' parameter")]
+ public ECPublicKeyParameters(
+ ECPoint q,
+ DerObjectIdentifier publicKeyParamSet)
+ : base("ECGOST3410", false, publicKeyParamSet)
+ {
+ if (q == null)
+ throw new ArgumentNullException("q");
+
+ this.q = q;
+ }
+
+ public ECPublicKeyParameters(
+ string algorithm,
+ ECPoint q,
+ ECDomainParameters parameters)
+ : base(algorithm, false, parameters)
+ {
+ if (q == null)
+ throw new ArgumentNullException("q");
+
+ this.q = q;
+ }
+
+ public ECPublicKeyParameters(
+ string algorithm,
+ ECPoint q,
+ DerObjectIdentifier publicKeyParamSet)
+ : base(algorithm, false, publicKeyParamSet)
+ {
+ if (q == null)
+ throw new ArgumentNullException("q");
+
+ this.q = q;
+ }
+
+ public ECPoint Q
+ {
+ get { return q; }
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == this)
+ return true;
+
+ ECPublicKeyParameters other = obj as ECPublicKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ ECPublicKeyParameters other)
+ {
+ return q.Equals(other.q) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return q.GetHashCode() ^ base.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ElGamalKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/ElGamalKeyGenerationParameters.cs
new file mode 100644
index 000000000..40ca70df4
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ElGamalKeyGenerationParameters.cs
@@ -0,0 +1,31 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ElGamalKeyGenerationParameters
+ : KeyGenerationParameters
+ {
+ private readonly ElGamalParameters parameters;
+
+ public ElGamalKeyGenerationParameters(
+ SecureRandom random,
+ ElGamalParameters parameters)
+ : base(random, GetStrength(parameters))
+ {
+ this.parameters = parameters;
+ }
+
+ public ElGamalParameters Parameters
+ {
+ get { return parameters; }
+ }
+
+ internal static int GetStrength(
+ ElGamalParameters parameters)
+ {
+ return parameters.L != 0 ? parameters.L : parameters.P.BitLength;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ElGamalKeyParameters.cs b/Crypto/src/crypto/parameters/ElGamalKeyParameters.cs
new file mode 100644
index 000000000..8b6e27957
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ElGamalKeyParameters.cs
@@ -0,0 +1,59 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ElGamalKeyParameters
+ : AsymmetricKeyParameter
+ {
+ private readonly ElGamalParameters parameters;
+
+ protected ElGamalKeyParameters(
+ bool isPrivate,
+ ElGamalParameters parameters)
+ : base(isPrivate)
+ {
+ // TODO Should we allow 'parameters' to be null?
+ this.parameters = parameters;
+ }
+
+ public ElGamalParameters Parameters
+ {
+ get { return parameters; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ ElGamalKeyParameters other = obj as ElGamalKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ ElGamalKeyParameters other)
+ {
+ return Platform.Equals(parameters, other.parameters)
+ && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ int hc = base.GetHashCode();
+
+ if (parameters != null)
+ {
+ hc ^= parameters.GetHashCode();
+ }
+
+ return hc;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ElGamalParameters.cs b/Crypto/src/crypto/parameters/ElGamalParameters.cs
new file mode 100644
index 000000000..ab6d3e710
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ElGamalParameters.cs
@@ -0,0 +1,81 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ElGamalParameters
+ : ICipherParameters
+ {
+ private readonly BigInteger p, g;
+ private readonly int l;
+
+ public ElGamalParameters(
+ BigInteger p,
+ BigInteger g)
+ : this(p, g, 0)
+ {
+ }
+
+ public ElGamalParameters(
+ BigInteger p,
+ BigInteger g,
+ int l)
+ {
+ if (p == null)
+ throw new ArgumentNullException("p");
+ if (g == null)
+ throw new ArgumentNullException("g");
+
+ this.p = p;
+ this.g = g;
+ this.l = l;
+ }
+
+ public BigInteger P
+ {
+ get { return p; }
+ }
+
+ /**
+ * return the generator - g
+ */
+ public BigInteger G
+ {
+ get { return g; }
+ }
+
+ /**
+ * return private value limit - l
+ */
+ public int L
+ {
+ get { return l; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ ElGamalParameters other = obj as ElGamalParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ ElGamalParameters other)
+ {
+ return p.Equals(other.p) && g.Equals(other.g) && l == other.l;
+ }
+
+ public override int GetHashCode()
+ {
+ return p.GetHashCode() ^ g.GetHashCode() ^ l;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ElGamalPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/ElGamalPrivateKeyParameters.cs
new file mode 100644
index 000000000..6363f2bbb
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ElGamalPrivateKeyParameters.cs
@@ -0,0 +1,53 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ElGamalPrivateKeyParameters
+ : ElGamalKeyParameters
+ {
+ private readonly BigInteger x;
+
+ public ElGamalPrivateKeyParameters(
+ BigInteger x,
+ ElGamalParameters parameters)
+ : base(true, parameters)
+ {
+ if (x == null)
+ throw new ArgumentNullException("x");
+
+ this.x = x;
+ }
+
+ public BigInteger X
+ {
+ get { return x; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ ElGamalPrivateKeyParameters other = obj as ElGamalPrivateKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ ElGamalPrivateKeyParameters other)
+ {
+ return other.x.Equals(x) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return x.GetHashCode() ^ base.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ElGamalPublicKeyParameters.cs b/Crypto/src/crypto/parameters/ElGamalPublicKeyParameters.cs
new file mode 100644
index 000000000..25ac625d5
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ElGamalPublicKeyParameters.cs
@@ -0,0 +1,53 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ElGamalPublicKeyParameters
+ : ElGamalKeyParameters
+ {
+ private readonly BigInteger y;
+
+ public ElGamalPublicKeyParameters(
+ BigInteger y,
+ ElGamalParameters parameters)
+ : base(false, parameters)
+ {
+ if (y == null)
+ throw new ArgumentNullException("y");
+
+ this.y = y;
+ }
+
+ public BigInteger Y
+ {
+ get { return y; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ ElGamalPublicKeyParameters other = obj as ElGamalPublicKeyParameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ ElGamalPublicKeyParameters other)
+ {
+ return y.Equals(other.y) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return y.GetHashCode() ^ base.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/GOST3410KeyGenerationParameters.cs b/Crypto/src/crypto/parameters/GOST3410KeyGenerationParameters.cs
new file mode 100644
index 000000000..b06a5d896
--- /dev/null
+++ b/Crypto/src/crypto/parameters/GOST3410KeyGenerationParameters.cs
@@ -0,0 +1,55 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class Gost3410KeyGenerationParameters
+ : KeyGenerationParameters
+ {
+ private readonly Gost3410Parameters parameters;
+ private readonly DerObjectIdentifier publicKeyParamSet;
+
+ public Gost3410KeyGenerationParameters(
+ SecureRandom random,
+ Gost3410Parameters parameters)
+ : base(random, parameters.P.BitLength - 1)
+ {
+ this.parameters = parameters;
+ }
+
+ public Gost3410KeyGenerationParameters(
+ SecureRandom random,
+ DerObjectIdentifier publicKeyParamSet)
+ : this(random, LookupParameters(publicKeyParamSet))
+ {
+ this.publicKeyParamSet = publicKeyParamSet;
+ }
+
+ public Gost3410Parameters Parameters
+ {
+ get { return parameters; }
+ }
+
+ public DerObjectIdentifier PublicKeyParamSet
+ {
+ get { return publicKeyParamSet; }
+ }
+
+ private static Gost3410Parameters LookupParameters(
+ DerObjectIdentifier publicKeyParamSet)
+ {
+ if (publicKeyParamSet == null)
+ throw new ArgumentNullException("publicKeyParamSet");
+
+ Gost3410ParamSetParameters p = Gost3410NamedParameters.GetByOid(publicKeyParamSet);
+
+ if (p == null)
+ throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet");
+
+ return new Gost3410Parameters(p.P, p.Q, p.A);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/GOST3410KeyParameters.cs b/Crypto/src/crypto/parameters/GOST3410KeyParameters.cs
new file mode 100644
index 000000000..f771c4d97
--- /dev/null
+++ b/Crypto/src/crypto/parameters/GOST3410KeyParameters.cs
@@ -0,0 +1,58 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public abstract class Gost3410KeyParameters
+ : AsymmetricKeyParameter
+ {
+ private readonly Gost3410Parameters parameters;
+ private readonly DerObjectIdentifier publicKeyParamSet;
+
+ protected Gost3410KeyParameters(
+ bool isPrivate,
+ Gost3410Parameters parameters)
+ : base(isPrivate)
+ {
+ this.parameters = parameters;
+ }
+
+ protected Gost3410KeyParameters(
+ bool isPrivate,
+ DerObjectIdentifier publicKeyParamSet)
+ : base(isPrivate)
+ {
+ this.parameters = LookupParameters(publicKeyParamSet);
+ this.publicKeyParamSet = publicKeyParamSet;
+ }
+
+ public Gost3410Parameters Parameters
+ {
+ get { return parameters; }
+ }
+
+ public DerObjectIdentifier PublicKeyParamSet
+ {
+ get { return publicKeyParamSet; }
+ }
+
+ // TODO Implement Equals/GetHashCode
+
+ private static Gost3410Parameters LookupParameters(
+ DerObjectIdentifier publicKeyParamSet)
+ {
+ if (publicKeyParamSet == null)
+ throw new ArgumentNullException("publicKeyParamSet");
+
+ Gost3410ParamSetParameters p = Gost3410NamedParameters.GetByOid(publicKeyParamSet);
+
+ if (p == null)
+ throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet");
+
+ return new Gost3410Parameters(p.P, p.Q, p.A);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/GOST3410Parameters.cs b/Crypto/src/crypto/parameters/GOST3410Parameters.cs
new file mode 100644
index 000000000..2ec167ef0
--- /dev/null
+++ b/Crypto/src/crypto/parameters/GOST3410Parameters.cs
@@ -0,0 +1,86 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class Gost3410Parameters
+ : ICipherParameters
+ {
+ private readonly BigInteger p, q, a;
+ private readonly Gost3410ValidationParameters validation;
+
+ public Gost3410Parameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger a)
+ : this(p, q, a, null)
+ {
+ }
+
+ public Gost3410Parameters(
+ BigInteger p,
+ BigInteger q,
+ BigInteger a,
+ Gost3410ValidationParameters validation)
+ {
+ if (p == null)
+ throw new ArgumentNullException("p");
+ if (q == null)
+ throw new ArgumentNullException("q");
+ if (a == null)
+ throw new ArgumentNullException("a");
+
+ this.p = p;
+ this.q = q;
+ this.a = a;
+ this.validation = validation;
+ }
+
+ public BigInteger P
+ {
+ get { return p; }
+ }
+
+ public BigInteger Q
+ {
+ get { return q; }
+ }
+
+ public BigInteger A
+ {
+ get { return a; }
+ }
+
+ public Gost3410ValidationParameters ValidationParameters
+ {
+ get { return validation; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ Gost3410Parameters other = obj as Gost3410Parameters;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ Gost3410Parameters other)
+ {
+ return p.Equals(other.p) && q.Equals(other.q) && a.Equals(other.a);
+ }
+
+ public override int GetHashCode()
+ {
+ return p.GetHashCode() ^ q.GetHashCode() ^ a.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/GOST3410PrivateKeyParameters.cs b/Crypto/src/crypto/parameters/GOST3410PrivateKeyParameters.cs
new file mode 100644
index 000000000..e3a613de6
--- /dev/null
+++ b/Crypto/src/crypto/parameters/GOST3410PrivateKeyParameters.cs
@@ -0,0 +1,41 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class Gost3410PrivateKeyParameters
+ : Gost3410KeyParameters
+ {
+ private readonly BigInteger x;
+
+ public Gost3410PrivateKeyParameters(
+ BigInteger x,
+ Gost3410Parameters parameters)
+ : base(true, parameters)
+ {
+ if (x.SignValue < 1 || x.BitLength > 256 || x.CompareTo(Parameters.Q) >= 0)
+ throw new ArgumentException("Invalid x for GOST3410 private key", "x");
+
+ this.x = x;
+ }
+
+ public Gost3410PrivateKeyParameters(
+ BigInteger x,
+ DerObjectIdentifier publicKeyParamSet)
+ : base(true, publicKeyParamSet)
+ {
+ if (x.SignValue < 1 || x.BitLength > 256 || x.CompareTo(Parameters.Q) >= 0)
+ throw new ArgumentException("Invalid x for GOST3410 private key", "x");
+
+ this.x = x;
+ }
+
+ public BigInteger X
+ {
+ get { return x; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/GOST3410PublicKeyParameters.cs b/Crypto/src/crypto/parameters/GOST3410PublicKeyParameters.cs
new file mode 100644
index 000000000..96b7e91ea
--- /dev/null
+++ b/Crypto/src/crypto/parameters/GOST3410PublicKeyParameters.cs
@@ -0,0 +1,40 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class Gost3410PublicKeyParameters
+ : Gost3410KeyParameters
+ {
+ private readonly BigInteger y;
+
+ public Gost3410PublicKeyParameters(
+ BigInteger y,
+ Gost3410Parameters parameters)
+ : base(false, parameters)
+ {
+ if (y.SignValue < 1 || y.CompareTo(Parameters.P) >= 0)
+ throw new ArgumentException("Invalid y for GOST3410 public key", "y");
+
+ this.y = y;
+ }
+
+ public Gost3410PublicKeyParameters(
+ BigInteger y,
+ DerObjectIdentifier publicKeyParamSet)
+ : base(false, publicKeyParamSet)
+ {
+ if (y.SignValue < 1 || y.CompareTo(Parameters.P) >= 0)
+ throw new ArgumentException("Invalid y for GOST3410 public key", "y");
+
+ this.y = y;
+ }
+
+ public BigInteger Y
+ {
+ get { return y; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/GOST3410ValidationParameters.cs b/Crypto/src/crypto/parameters/GOST3410ValidationParameters.cs
new file mode 100644
index 000000000..21e5af823
--- /dev/null
+++ b/Crypto/src/crypto/parameters/GOST3410ValidationParameters.cs
@@ -0,0 +1,51 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class Gost3410ValidationParameters
+ {
+ private int x0;
+ private int c;
+ private long x0L;
+ private long cL;
+
+ public Gost3410ValidationParameters(
+ int x0,
+ int c)
+ {
+ this.x0 = x0;
+ this.c = c;
+ }
+
+ public Gost3410ValidationParameters(
+ long x0L,
+ long cL)
+ {
+ this.x0L = x0L;
+ this.cL = cL;
+ }
+
+ public int C { get { return c; } }
+ public int X0 { get { return x0; } }
+ public long CL { get { return cL; } }
+ public long X0L { get { return x0L; } }
+
+ public override bool Equals(
+ object obj)
+ {
+ Gost3410ValidationParameters other = obj as Gost3410ValidationParameters;
+
+ return other != null
+ && other.c == this.c
+ && other.x0 == this.x0
+ && other.cL == this.cL
+ && other.x0L == this.x0L;
+ }
+
+ public override int GetHashCode()
+ {
+ return c.GetHashCode() ^ x0.GetHashCode() ^ cL.GetHashCode() ^ x0L.GetHashCode();
+ }
+
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ISO18033KDFParameters.cs b/Crypto/src/crypto/parameters/ISO18033KDFParameters.cs
new file mode 100644
index 000000000..2d8fff8e3
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ISO18033KDFParameters.cs
@@ -0,0 +1,25 @@
+using System;
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ /**
+ * parameters for Key derivation functions for ISO-18033
+ */
+ public class Iso18033KdfParameters
+ : IDerivationParameters
+ {
+ byte[] seed;
+
+ public Iso18033KdfParameters(
+ byte[] seed)
+ {
+ this.seed = seed;
+ }
+
+ public byte[] GetSeed()
+ {
+ return seed;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/IesParameters.cs b/Crypto/src/crypto/parameters/IesParameters.cs
new file mode 100644
index 000000000..d306b2c33
--- /dev/null
+++ b/Crypto/src/crypto/parameters/IesParameters.cs
@@ -0,0 +1,49 @@
+using System;
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ /**
+ * parameters for using an integrated cipher in stream mode.
+ */
+ public class IesParameters : ICipherParameters
+ {
+ private byte[] derivation;
+ private byte[] encoding;
+ private int macKeySize;
+
+ /**
+ * @param derivation the derivation parameter for the KDF function.
+ * @param encoding the encoding parameter for the KDF function.
+ * @param macKeySize the size of the MAC key (in bits).
+ */
+ public IesParameters(
+ byte[] derivation,
+ byte[] encoding,
+ int macKeySize)
+ {
+ this.derivation = derivation;
+ this.encoding = encoding;
+ this.macKeySize = macKeySize;
+ }
+
+ public byte[] GetDerivationV()
+ {
+ return derivation;
+ }
+
+ public byte[] GetEncodingV()
+ {
+ return encoding;
+ }
+
+ public int MacKeySize
+ {
+ get
+ {
+ return macKeySize;
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/parameters/IesWithCipherParameters.cs b/Crypto/src/crypto/parameters/IesWithCipherParameters.cs
new file mode 100644
index 000000000..70ef55d54
--- /dev/null
+++ b/Crypto/src/crypto/parameters/IesWithCipherParameters.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class IesWithCipherParameters : IesParameters
+ {
+ private int cipherKeySize;
+
+ /**
+ * @param derivation the derivation parameter for the KDF function.
+ * @param encoding the encoding parameter for the KDF function.
+ * @param macKeySize the size of the MAC key (in bits).
+ * @param cipherKeySize the size of the associated Cipher key (in bits).
+ */
+ public IesWithCipherParameters(
+ byte[] derivation,
+ byte[] encoding,
+ int macKeySize,
+ int cipherKeySize) : base(derivation, encoding, macKeySize)
+ {
+ this.cipherKeySize = cipherKeySize;
+ }
+
+ public int CipherKeySize
+ {
+ get
+ {
+ return cipherKeySize;
+ }
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/parameters/KdfParameters.cs b/Crypto/src/crypto/parameters/KdfParameters.cs
new file mode 100644
index 000000000..bc5c905d0
--- /dev/null
+++ b/Crypto/src/crypto/parameters/KdfParameters.cs
@@ -0,0 +1,33 @@
+using System;
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ /**
+ * parameters for Key derivation functions for IEEE P1363a
+ */
+ public class KdfParameters : IDerivationParameters
+ {
+ byte[] iv;
+ byte[] shared;
+
+ public KdfParameters(
+ byte[] shared,
+ byte[] iv)
+ {
+ this.shared = shared;
+ this.iv = iv;
+ }
+
+ public byte[] GetSharedSecret()
+ {
+ return shared;
+ }
+
+ public byte[] GetIV()
+ {
+ return iv;
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/parameters/KeyParameter.cs b/Crypto/src/crypto/parameters/KeyParameter.cs
new file mode 100644
index 000000000..33dff96d7
--- /dev/null
+++ b/Crypto/src/crypto/parameters/KeyParameter.cs
@@ -0,0 +1,43 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class KeyParameter
+ : ICipherParameters
+ {
+ private readonly byte[] key;
+
+ public KeyParameter(
+ byte[] key)
+ {
+ if (key == null)
+ throw new ArgumentNullException("key");
+
+ this.key = (byte[]) key.Clone();
+ }
+
+ public KeyParameter(
+ byte[] key,
+ int keyOff,
+ int keyLen)
+ {
+ if (key == null)
+ throw new ArgumentNullException("key");
+ if (keyOff < 0 || keyOff > key.Length)
+ throw new ArgumentOutOfRangeException("keyOff");
+ if (keyLen < 0 || (keyOff + keyLen) > key.Length)
+ throw new ArgumentOutOfRangeException("keyLen");
+
+ this.key = new byte[keyLen];
+ Array.Copy(key, keyOff, this.key, 0, keyLen);
+ }
+
+ public byte[] GetKey()
+ {
+ return (byte[]) key.Clone();
+ }
+ }
+
+}
diff --git a/Crypto/src/crypto/parameters/MgfParameters.cs b/Crypto/src/crypto/parameters/MgfParameters.cs
new file mode 100644
index 000000000..11983b877
--- /dev/null
+++ b/Crypto/src/crypto/parameters/MgfParameters.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ /// <remarks>Parameters for mask derivation functions.</remarks>
+ public class MgfParameters
+ : IDerivationParameters
+ {
+ private readonly byte[] seed;
+
+ public MgfParameters(
+ byte[] seed)
+ : this(seed, 0, seed.Length)
+ {
+ }
+
+ public MgfParameters(
+ byte[] seed,
+ int off,
+ int len)
+ {
+ this.seed = new byte[len];
+ Array.Copy(seed, off, this.seed, 0, len);
+ }
+
+ public byte[] GetSeed()
+ {
+ return (byte[]) seed.Clone();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/MqvPrivateParameters.cs b/Crypto/src/crypto/parameters/MqvPrivateParameters.cs
new file mode 100644
index 000000000..4bf33e347
--- /dev/null
+++ b/Crypto/src/crypto/parameters/MqvPrivateParameters.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class MqvPrivateParameters
+ : ICipherParameters
+ {
+ private readonly ECPrivateKeyParameters staticPrivateKey;
+ private readonly ECPrivateKeyParameters ephemeralPrivateKey;
+ private readonly ECPublicKeyParameters ephemeralPublicKey;
+
+ public MqvPrivateParameters(
+ ECPrivateKeyParameters staticPrivateKey,
+ ECPrivateKeyParameters ephemeralPrivateKey)
+ : this(staticPrivateKey, ephemeralPrivateKey, null)
+ {
+ }
+
+ public MqvPrivateParameters(
+ ECPrivateKeyParameters staticPrivateKey,
+ ECPrivateKeyParameters ephemeralPrivateKey,
+ ECPublicKeyParameters ephemeralPublicKey)
+ {
+ this.staticPrivateKey = staticPrivateKey;
+ this.ephemeralPrivateKey = ephemeralPrivateKey;
+ this.ephemeralPublicKey = ephemeralPublicKey;
+ }
+
+ public ECPrivateKeyParameters StaticPrivateKey
+ {
+ get { return staticPrivateKey; }
+ }
+
+ public ECPrivateKeyParameters EphemeralPrivateKey
+ {
+ get { return ephemeralPrivateKey; }
+ }
+
+ public ECPublicKeyParameters EphemeralPublicKey
+ {
+ get { return ephemeralPublicKey; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/MqvPublicParameters.cs b/Crypto/src/crypto/parameters/MqvPublicParameters.cs
new file mode 100644
index 000000000..a0e273ac4
--- /dev/null
+++ b/Crypto/src/crypto/parameters/MqvPublicParameters.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class MqvPublicParameters
+ : ICipherParameters
+ {
+ private readonly ECPublicKeyParameters staticPublicKey;
+ private readonly ECPublicKeyParameters ephemeralPublicKey;
+
+ public MqvPublicParameters(
+ ECPublicKeyParameters staticPublicKey,
+ ECPublicKeyParameters ephemeralPublicKey)
+ {
+ this.staticPublicKey = staticPublicKey;
+ this.ephemeralPublicKey = ephemeralPublicKey;
+ }
+
+ public ECPublicKeyParameters StaticPublicKey
+ {
+ get { return staticPublicKey; }
+ }
+
+ public ECPublicKeyParameters EphemeralPublicKey
+ {
+ get { return ephemeralPublicKey; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/NaccacheSternKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/NaccacheSternKeyGenerationParameters.cs
new file mode 100644
index 000000000..5b4052505
--- /dev/null
+++ b/Crypto/src/crypto/parameters/NaccacheSternKeyGenerationParameters.cs
@@ -0,0 +1,101 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ /**
+ * Parameters for NaccacheStern public private key generation. For details on
+ * this cipher, please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+ public class NaccacheSternKeyGenerationParameters : KeyGenerationParameters
+ {
+ // private BigInteger publicExponent;
+ private readonly int certainty;
+ private readonly int countSmallPrimes;
+ private bool debug;
+
+ /**
+ * Parameters for generating a NaccacheStern KeyPair.
+ *
+ * @param random
+ * The source of randomness
+ * @param strength
+ * The desired strength of the Key in Bits
+ * @param certainty
+ * the probability that the generated primes are not really prime
+ * as integer: 2^(-certainty) is then the probability
+ * @param countSmallPrimes
+ * How many small key factors are desired
+ */
+ public NaccacheSternKeyGenerationParameters(
+ SecureRandom random,
+ int strength,
+ int certainty,
+ int countSmallPrimes)
+ : this(random, strength, certainty, countSmallPrimes, false)
+ {
+ }
+
+ /**
+ * Parameters for a NaccacheStern KeyPair.
+ *
+ * @param random
+ * The source of randomness
+ * @param strength
+ * The desired strength of the Key in Bits
+ * @param certainty
+ * the probability that the generated primes are not really prime
+ * as integer: 2^(-certainty) is then the probability
+ * @param cntSmallPrimes
+ * How many small key factors are desired
+ * @param debug
+ * Turn debugging on or off (reveals secret information, use with
+ * caution)
+ */
+ public NaccacheSternKeyGenerationParameters(SecureRandom random,
+ int strength,
+ int certainty,
+ int countSmallPrimes,
+ bool debug)
+ : base(random, strength)
+ {
+ if (countSmallPrimes % 2 == 1)
+ {
+ throw new ArgumentException("countSmallPrimes must be a multiple of 2");
+ }
+ if (countSmallPrimes < 30)
+ {
+ throw new ArgumentException("countSmallPrimes must be >= 30 for security reasons");
+ }
+ this.certainty = certainty;
+ this.countSmallPrimes = countSmallPrimes;
+ this.debug = debug;
+ }
+
+ /**
+ * @return Returns the certainty.
+ */
+ public int Certainty
+ {
+ get { return certainty; }
+ }
+
+ /**
+ * @return Returns the countSmallPrimes.
+ */
+ public int CountSmallPrimes
+ {
+ get { return countSmallPrimes; }
+ }
+
+ public bool IsDebug
+ {
+ get { return debug; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/NaccacheSternKeyParameters.cs b/Crypto/src/crypto/parameters/NaccacheSternKeyParameters.cs
new file mode 100644
index 000000000..8be7ad835
--- /dev/null
+++ b/Crypto/src/crypto/parameters/NaccacheSternKeyParameters.cs
@@ -0,0 +1,44 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ /**
+ * Public key parameters for NaccacheStern cipher. For details on this cipher,
+ * please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+ public class NaccacheSternKeyParameters : AsymmetricKeyParameter
+ {
+ private readonly BigInteger g, n;
+ private readonly int lowerSigmaBound;
+
+ /**
+ * @param privateKey
+ */
+ public NaccacheSternKeyParameters(bool privateKey, BigInteger g, BigInteger n, int lowerSigmaBound)
+ : base(privateKey)
+ {
+ this.g = g;
+ this.n = n;
+ this.lowerSigmaBound = lowerSigmaBound;
+ }
+
+ /**
+ * @return Returns the g.
+ */
+ public BigInteger G { get { return g; } }
+
+ /**
+ * @return Returns the lowerSigmaBound.
+ */
+ public int LowerSigmaBound { get { return lowerSigmaBound; } }
+
+ /**
+ * @return Returns the n.
+ */
+ public BigInteger Modulus { get { return n; } }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/NaccacheSternPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/NaccacheSternPrivateKeyParameters.cs
new file mode 100644
index 000000000..42a0454a1
--- /dev/null
+++ b/Crypto/src/crypto/parameters/NaccacheSternPrivateKeyParameters.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ /**
+ * Private key parameters for NaccacheStern cipher. For details on this cipher,
+ * please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+ public class NaccacheSternPrivateKeyParameters : NaccacheSternKeyParameters
+ {
+ private readonly BigInteger phiN;
+ private readonly IList smallPrimes;
+
+#if !(SILVERLIGHT || PORTABLE)
+ [Obsolete]
+ public NaccacheSternPrivateKeyParameters(
+ BigInteger g,
+ BigInteger n,
+ int lowerSigmaBound,
+ ArrayList smallPrimes,
+ BigInteger phiN)
+ : base(true, g, n, lowerSigmaBound)
+ {
+ this.smallPrimes = smallPrimes;
+ this.phiN = phiN;
+ }
+#endif
+
+ /**
+ * Constructs a NaccacheSternPrivateKey
+ *
+ * @param g
+ * the public enryption parameter g
+ * @param n
+ * the public modulus n = p*q
+ * @param lowerSigmaBound
+ * the public lower sigma bound up to which data can be encrypted
+ * @param smallPrimes
+ * the small primes, of which sigma is constructed in the right
+ * order
+ * @param phi_n
+ * the private modulus phi(n) = (p-1)(q-1)
+ */
+ public NaccacheSternPrivateKeyParameters(
+ BigInteger g,
+ BigInteger n,
+ int lowerSigmaBound,
+ IList smallPrimes,
+ BigInteger phiN)
+ : base(true, g, n, lowerSigmaBound)
+ {
+ this.smallPrimes = smallPrimes;
+ this.phiN = phiN;
+ }
+
+ public BigInteger PhiN
+ {
+ get { return phiN; }
+ }
+
+#if !(SILVERLIGHT || PORTABLE)
+ [Obsolete("Use 'SmallPrimesList' instead")]
+ public ArrayList SmallPrimes
+ {
+ get { return new ArrayList(smallPrimes); }
+ }
+#endif
+
+ public IList SmallPrimesList
+ {
+ get { return smallPrimes; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ParametersWithIV.cs b/Crypto/src/crypto/parameters/ParametersWithIV.cs
new file mode 100644
index 000000000..e00abce58
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ParametersWithIV.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ParametersWithIV
+ : ICipherParameters
+ {
+ private readonly ICipherParameters parameters;
+ private readonly byte[] iv;
+
+ public ParametersWithIV(
+ ICipherParameters parameters,
+ byte[] iv)
+ : this(parameters, iv, 0, iv.Length)
+ {
+ }
+
+ public ParametersWithIV(
+ ICipherParameters parameters,
+ byte[] iv,
+ int ivOff,
+ int ivLen)
+ {
+ if (parameters == null)
+ throw new ArgumentNullException("parameters");
+ if (iv == null)
+ throw new ArgumentNullException("iv");
+
+ this.parameters = parameters;
+ this.iv = new byte[ivLen];
+ Array.Copy(iv, ivOff, this.iv, 0, ivLen);
+ }
+
+ public byte[] GetIV()
+ {
+ return (byte[]) iv.Clone();
+ }
+
+ public ICipherParameters Parameters
+ {
+ get { return parameters; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ParametersWithRandom.cs b/Crypto/src/crypto/parameters/ParametersWithRandom.cs
new file mode 100644
index 000000000..a05e77409
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ParametersWithRandom.cs
@@ -0,0 +1,48 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ParametersWithRandom
+ : ICipherParameters
+ {
+ private readonly ICipherParameters parameters;
+ private readonly SecureRandom random;
+
+ public ParametersWithRandom(
+ ICipherParameters parameters,
+ SecureRandom random)
+ {
+ if (parameters == null)
+ throw new ArgumentNullException("random");
+ if (random == null)
+ throw new ArgumentNullException("random");
+
+ this.parameters = parameters;
+ this.random = random;
+ }
+
+ public ParametersWithRandom(
+ ICipherParameters parameters)
+ : this(parameters, new SecureRandom())
+ {
+ }
+
+ [Obsolete("Use Random property instead")]
+ public SecureRandom GetRandom()
+ {
+ return Random;
+ }
+
+ public SecureRandom Random
+ {
+ get { return random; }
+ }
+
+ public ICipherParameters Parameters
+ {
+ get { return parameters; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ParametersWithSBox.cs b/Crypto/src/crypto/parameters/ParametersWithSBox.cs
new file mode 100644
index 000000000..6473796e3
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ParametersWithSBox.cs
@@ -0,0 +1,24 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ParametersWithSBox : ICipherParameters
+ {
+ private ICipherParameters parameters;
+ private byte[] sBox;
+
+ public ParametersWithSBox(
+ ICipherParameters parameters,
+ byte[] sBox)
+ {
+ this.parameters = parameters;
+ this.sBox = sBox;
+ }
+
+ public byte[] GetSBox() { return sBox; }
+
+ public ICipherParameters Parameters { get { return parameters; } }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/ParametersWithSalt.cs b/Crypto/src/crypto/parameters/ParametersWithSalt.cs
new file mode 100644
index 000000000..7f4cd6cd1
--- /dev/null
+++ b/Crypto/src/crypto/parameters/ParametersWithSalt.cs
@@ -0,0 +1,39 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+
+ /// <summary> Cipher parameters with a fixed salt value associated with them.</summary>
+ public class ParametersWithSalt : ICipherParameters
+ {
+ private byte[] salt;
+ private ICipherParameters parameters;
+
+ public ParametersWithSalt(ICipherParameters parameters, byte[] salt):this(parameters, salt, 0, salt.Length)
+ {
+ }
+
+ public ParametersWithSalt(ICipherParameters parameters, byte[] salt, int saltOff, int saltLen)
+ {
+ this.salt = new byte[saltLen];
+ this.parameters = parameters;
+
+ Array.Copy(salt, saltOff, this.salt, 0, saltLen);
+ }
+
+ public byte[] GetSalt()
+ {
+ return salt;
+ }
+
+ public ICipherParameters Parameters
+ {
+ get
+ {
+ return parameters;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/RC2Parameters.cs b/Crypto/src/crypto/parameters/RC2Parameters.cs
new file mode 100644
index 000000000..7a6d5bb6e
--- /dev/null
+++ b/Crypto/src/crypto/parameters/RC2Parameters.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class RC2Parameters
+ : KeyParameter
+ {
+ private readonly int bits;
+
+ public RC2Parameters(
+ byte[] key)
+ : this(key, (key.Length > 128) ? 1024 : (key.Length * 8))
+ {
+ }
+
+ public RC2Parameters(
+ byte[] key,
+ int keyOff,
+ int keyLen)
+ : this(key, keyOff, keyLen, (keyLen > 128) ? 1024 : (keyLen * 8))
+ {
+ }
+
+ public RC2Parameters(
+ byte[] key,
+ int bits)
+ : base(key)
+ {
+ this.bits = bits;
+ }
+
+ public RC2Parameters(
+ byte[] key,
+ int keyOff,
+ int keyLen,
+ int bits)
+ : base(key, keyOff, keyLen)
+ {
+ this.bits = bits;
+ }
+
+ public int EffectiveKeyBits
+ {
+ get { return bits; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/RC5Parameters.cs b/Crypto/src/crypto/parameters/RC5Parameters.cs
new file mode 100644
index 000000000..88a59e197
--- /dev/null
+++ b/Crypto/src/crypto/parameters/RC5Parameters.cs
@@ -0,0 +1,27 @@
+using System;
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class RC5Parameters
+ : KeyParameter
+ {
+ private readonly int rounds;
+
+ public RC5Parameters(
+ byte[] key,
+ int rounds)
+ : base(key)
+ {
+ if (key.Length > 255)
+ throw new ArgumentException("RC5 key length can be no greater than 255");
+
+ this.rounds = rounds;
+ }
+
+ public int Rounds
+ {
+ get { return rounds; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/RSABlindingParameters.cs b/Crypto/src/crypto/parameters/RSABlindingParameters.cs
new file mode 100644
index 000000000..49c7bcce6
--- /dev/null
+++ b/Crypto/src/crypto/parameters/RSABlindingParameters.cs
@@ -0,0 +1,34 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class RsaBlindingParameters
+ : ICipherParameters
+ {
+ private readonly RsaKeyParameters publicKey;
+ private readonly BigInteger blindingFactor;
+
+ public RsaBlindingParameters(
+ RsaKeyParameters publicKey,
+ BigInteger blindingFactor)
+ {
+ if (publicKey.IsPrivate)
+ throw new ArgumentException("RSA parameters should be for a public key");
+
+ this.publicKey = publicKey;
+ this.blindingFactor = blindingFactor;
+ }
+
+ public RsaKeyParameters PublicKey
+ {
+ get { return publicKey; }
+ }
+
+ public BigInteger BlindingFactor
+ {
+ get { return blindingFactor; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/RsaKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/RsaKeyGenerationParameters.cs
new file mode 100644
index 000000000..619ab65b4
--- /dev/null
+++ b/Crypto/src/crypto/parameters/RsaKeyGenerationParameters.cs
@@ -0,0 +1,55 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class RsaKeyGenerationParameters
+ : KeyGenerationParameters
+ {
+ private readonly BigInteger publicExponent;
+ private readonly int certainty;
+
+ public RsaKeyGenerationParameters(
+ BigInteger publicExponent,
+ SecureRandom random,
+ int strength,
+ int certainty)
+ : base(random, strength)
+ {
+ this.publicExponent = publicExponent;
+ this.certainty = certainty;
+ }
+
+ public BigInteger PublicExponent
+ {
+ get { return publicExponent; }
+ }
+
+ public int Certainty
+ {
+ get { return certainty; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ RsaKeyGenerationParameters other = obj as RsaKeyGenerationParameters;
+
+ if (other == null)
+ {
+ return false;
+ }
+
+ return certainty == other.certainty
+ && publicExponent.Equals(other.publicExponent);
+ }
+
+ public override int GetHashCode()
+ {
+ return certainty.GetHashCode() ^ publicExponent.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/RsaKeyParameters.cs b/Crypto/src/crypto/parameters/RsaKeyParameters.cs
new file mode 100644
index 000000000..72c0d806f
--- /dev/null
+++ b/Crypto/src/crypto/parameters/RsaKeyParameters.cs
@@ -0,0 +1,63 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class RsaKeyParameters
+ : AsymmetricKeyParameter
+ {
+ private readonly BigInteger modulus;
+ private readonly BigInteger exponent;
+
+ public RsaKeyParameters(
+ bool isPrivate,
+ BigInteger modulus,
+ BigInteger exponent)
+ : base(isPrivate)
+ {
+ if (modulus == null)
+ throw new ArgumentNullException("modulus");
+ if (exponent == null)
+ throw new ArgumentNullException("exponent");
+ if (modulus.SignValue <= 0)
+ throw new ArgumentException("Not a valid RSA modulus", "modulus");
+ if (exponent.SignValue <= 0)
+ throw new ArgumentException("Not a valid RSA exponent", "exponent");
+
+ this.modulus = modulus;
+ this.exponent = exponent;
+ }
+
+ public BigInteger Modulus
+ {
+ get { return modulus; }
+ }
+
+ public BigInteger Exponent
+ {
+ get { return exponent; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ RsaKeyParameters kp = obj as RsaKeyParameters;
+
+ if (kp == null)
+ {
+ return false;
+ }
+
+ return kp.IsPrivate == this.IsPrivate
+ && kp.Modulus.Equals(this.modulus)
+ && kp.Exponent.Equals(this.exponent);
+ }
+
+ public override int GetHashCode()
+ {
+ return modulus.GetHashCode() ^ exponent.GetHashCode() ^ IsPrivate.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs b/Crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs
new file mode 100644
index 000000000..7bd8abd76
--- /dev/null
+++ b/Crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs
@@ -0,0 +1,104 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class RsaPrivateCrtKeyParameters
+ : RsaKeyParameters
+ {
+ private readonly BigInteger e, p, q, dP, dQ, qInv;
+
+ public RsaPrivateCrtKeyParameters(
+ BigInteger modulus,
+ BigInteger publicExponent,
+ BigInteger privateExponent,
+ BigInteger p,
+ BigInteger q,
+ BigInteger dP,
+ BigInteger dQ,
+ BigInteger qInv)
+ : base(true, modulus, privateExponent)
+ {
+ ValidateValue(publicExponent, "publicExponent", "exponent");
+ ValidateValue(p, "p", "P value");
+ ValidateValue(q, "q", "Q value");
+ ValidateValue(dP, "dP", "DP value");
+ ValidateValue(dQ, "dQ", "DQ value");
+ ValidateValue(qInv, "qInv", "InverseQ value");
+
+ this.e = publicExponent;
+ this.p = p;
+ this.q = q;
+ this.dP = dP;
+ this.dQ = dQ;
+ this.qInv = qInv;
+ }
+
+ public BigInteger PublicExponent
+ {
+ get { return e; }
+ }
+
+ public BigInteger P
+ {
+ get { return p; }
+ }
+
+ public BigInteger Q
+ {
+ get { return q; }
+ }
+
+ public BigInteger DP
+ {
+ get { return dP; }
+ }
+
+ public BigInteger DQ
+ {
+ get { return dQ; }
+ }
+
+ public BigInteger QInv
+ {
+ get { return qInv; }
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ RsaPrivateCrtKeyParameters kp = obj as RsaPrivateCrtKeyParameters;
+
+ if (kp == null)
+ return false;
+
+ return kp.DP.Equals(dP)
+ && kp.DQ.Equals(dQ)
+ && kp.Exponent.Equals(this.Exponent)
+ && kp.Modulus.Equals(this.Modulus)
+ && kp.P.Equals(p)
+ && kp.Q.Equals(q)
+ && kp.PublicExponent.Equals(e)
+ && kp.QInv.Equals(qInv);
+ }
+
+ public override int GetHashCode()
+ {
+ return DP.GetHashCode() ^ DQ.GetHashCode() ^ Exponent.GetHashCode() ^ Modulus.GetHashCode()
+ ^ P.GetHashCode() ^ Q.GetHashCode() ^ PublicExponent.GetHashCode() ^ QInv.GetHashCode();
+ }
+
+ private static void ValidateValue(BigInteger x, string name, string desc)
+ {
+ if (x == null)
+ throw new ArgumentNullException(name);
+ if (x.SignValue <= 0)
+ throw new ArgumentException("Not a valid RSA " + desc, name);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/Crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
new file mode 100644
index 000000000..9e9e29cf1
--- /dev/null
+++ b/Crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
@@ -0,0 +1,66 @@
+#if !(NETCF_1_0 || PORTABLE)
+
+using System;
+using System.Security.Cryptography;
+
+namespace Org.BouncyCastle.Crypto.Prng
+{
+ /// <summary>
+ /// Uses Microsoft's RNGCryptoServiceProvider
+ /// </summary>
+ public class CryptoApiRandomGenerator
+ : IRandomGenerator
+ {
+ private readonly RandomNumberGenerator rndProv;
+
+ public CryptoApiRandomGenerator()
+ : this(new RNGCryptoServiceProvider())
+ {
+ }
+
+ public CryptoApiRandomGenerator(RandomNumberGenerator rng)
+ {
+ this.rndProv = rng;
+ }
+
+ #region IRandomGenerator Members
+
+ public virtual void AddSeedMaterial(byte[] seed)
+ {
+ // We don't care about the seed
+ }
+
+ public virtual void AddSeedMaterial(long seed)
+ {
+ // We don't care about the seed
+ }
+
+ public virtual void NextBytes(byte[] bytes)
+ {
+ rndProv.GetBytes(bytes);
+ }
+
+ public virtual void NextBytes(byte[] bytes, int start, int len)
+ {
+ if (start < 0)
+ throw new ArgumentException("Start offset cannot be negative", "start");
+ if (bytes.Length < (start + len))
+ throw new ArgumentException("Byte array too small for requested offset and length");
+
+ if (bytes.Length == len && start == 0)
+ {
+ NextBytes(bytes);
+ }
+ else
+ {
+ byte[] tmpBuf = new byte[len];
+ rndProv.GetBytes(tmpBuf);
+ Array.Copy(tmpBuf, 0, bytes, start, len);
+ }
+ }
+
+ #endregion
+ }
+}
+
+#endif
diff --git a/Crypto/src/crypto/prng/DigestRandomGenerator.cs b/Crypto/src/crypto/prng/DigestRandomGenerator.cs
new file mode 100644
index 000000000..cbd2ef060
--- /dev/null
+++ b/Crypto/src/crypto/prng/DigestRandomGenerator.cs
@@ -0,0 +1,129 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+
+namespace Org.BouncyCastle.Crypto.Prng
+{
+ /**
+ * Random generation based on the digest with counter. Calling AddSeedMaterial will
+ * always increase the entropy of the hash.
+ * <p>
+ * Internal access to the digest is synchronized so a single one of these can be shared.
+ * </p>
+ */
+ public class DigestRandomGenerator
+ : IRandomGenerator
+ {
+ private const long CYCLE_COUNT = 10;
+
+ private long stateCounter;
+ private long seedCounter;
+ private IDigest digest;
+ private byte[] state;
+ private byte[] seed;
+
+ public DigestRandomGenerator(
+ IDigest digest)
+ {
+ this.digest = digest;
+
+ this.seed = new byte[digest.GetDigestSize()];
+ this.seedCounter = 1;
+
+ this.state = new byte[digest.GetDigestSize()];
+ this.stateCounter = 1;
+ }
+
+ public void AddSeedMaterial(
+ byte[] inSeed)
+ {
+ lock (this)
+ {
+ DigestUpdate(inSeed);
+ DigestUpdate(seed);
+ DigestDoFinal(seed);
+ }
+ }
+
+ public void AddSeedMaterial(
+ long rSeed)
+ {
+ lock (this)
+ {
+ DigestAddCounter(rSeed);
+ DigestUpdate(seed);
+ DigestDoFinal(seed);
+ }
+ }
+
+ public void NextBytes(
+ byte[] bytes)
+ {
+ NextBytes(bytes, 0, bytes.Length);
+ }
+
+ public void NextBytes(
+ byte[] bytes,
+ int start,
+ int len)
+ {
+ lock (this)
+ {
+ int stateOff = 0;
+
+ GenerateState();
+
+ int end = start + len;
+ for (int i = start; i < end; ++i)
+ {
+ if (stateOff == state.Length)
+ {
+ GenerateState();
+ stateOff = 0;
+ }
+ bytes[i] = state[stateOff++];
+ }
+ }
+ }
+
+ private void CycleSeed()
+ {
+ DigestUpdate(seed);
+ DigestAddCounter(seedCounter++);
+ DigestDoFinal(seed);
+ }
+
+ private void GenerateState()
+ {
+ DigestAddCounter(stateCounter++);
+ DigestUpdate(state);
+ DigestUpdate(seed);
+ DigestDoFinal(state);
+
+ if ((stateCounter % CYCLE_COUNT) == 0)
+ {
+ CycleSeed();
+ }
+ }
+
+ private void DigestAddCounter(long seedVal)
+ {
+ ulong seed = (ulong)seedVal;
+ for (int i = 0; i != 8; i++)
+ {
+ digest.Update((byte)seed);
+ seed >>= 8;
+ }
+ }
+
+ private void DigestUpdate(byte[] inSeed)
+ {
+ digest.BlockUpdate(inSeed, 0, inSeed.Length);
+ }
+
+ private void DigestDoFinal(byte[] result)
+ {
+ digest.DoFinal(result, 0);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/prng/IRandomGenerator.cs b/Crypto/src/crypto/prng/IRandomGenerator.cs
new file mode 100644
index 000000000..8dbe4068f
--- /dev/null
+++ b/Crypto/src/crypto/prng/IRandomGenerator.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Prng
+{
+ /// <remarks>Generic interface for objects generating random bytes.</remarks>
+ public interface IRandomGenerator
+ {
+ /// <summary>Add more seed material to the generator.</summary>
+ /// <param name="seed">A byte array to be mixed into the generator's state.</param>
+ void AddSeedMaterial(byte[] seed);
+
+ /// <summary>Add more seed material to the generator.</summary>
+ /// <param name="seed">A long value to be mixed into the generator's state.</param>
+ void AddSeedMaterial(long seed);
+
+ /// <summary>Fill byte array with random values.</summary>
+ /// <param name="bytes">Array to be filled.</param>
+ void NextBytes(byte[] bytes);
+
+ /// <summary>Fill byte array with random values.</summary>
+ /// <param name="bytes">Array to receive bytes.</param>
+ /// <param name="start">Index to start filling at.</param>
+ /// <param name="len">Length of segment to fill.</param>
+ void NextBytes(byte[] bytes, int start, int len);
+ }
+}
diff --git a/Crypto/src/crypto/prng/ReversedWindowGenerator.cs b/Crypto/src/crypto/prng/ReversedWindowGenerator.cs
new file mode 100644
index 000000000..dd28c525a
--- /dev/null
+++ b/Crypto/src/crypto/prng/ReversedWindowGenerator.cs
@@ -0,0 +1,98 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Prng
+{
+ /// <remarks>
+ /// Takes bytes generated by an underling RandomGenerator and reverses the order in
+ /// each small window (of configurable size).
+ /// <p>
+ /// Access to internals is synchronized so a single one of these can be shared.
+ /// </p>
+ /// </remarks>
+ public class ReversedWindowGenerator
+ : IRandomGenerator
+ {
+ private readonly IRandomGenerator generator;
+
+ private byte[] window;
+ private int windowCount;
+
+ public ReversedWindowGenerator(
+ IRandomGenerator generator,
+ int windowSize)
+ {
+ if (generator == null)
+ throw new ArgumentNullException("generator");
+ if (windowSize < 2)
+ throw new ArgumentException("Window size must be at least 2", "windowSize");
+
+ this.generator = generator;
+ this.window = new byte[windowSize];
+ }
+
+ /// <summary>Add more seed material to the generator.</summary>
+ /// <param name="seed">A byte array to be mixed into the generator's state.</param>
+ public virtual void AddSeedMaterial(
+ byte[] seed)
+ {
+ lock (this)
+ {
+ windowCount = 0;
+ generator.AddSeedMaterial(seed);
+ }
+ }
+
+ /// <summary>Add more seed material to the generator.</summary>
+ /// <param name="seed">A long value to be mixed into the generator's state.</param>
+ public virtual void AddSeedMaterial(
+ long seed)
+ {
+ lock (this)
+ {
+ windowCount = 0;
+ generator.AddSeedMaterial(seed);
+ }
+ }
+
+ /// <summary>Fill byte array with random values.</summary>
+ /// <param name="bytes">Array to be filled.</param>
+ public virtual void NextBytes(
+ byte[] bytes)
+ {
+ doNextBytes(bytes, 0, bytes.Length);
+ }
+
+ /// <summary>Fill byte array with random values.</summary>
+ /// <param name="bytes">Array to receive bytes.</param>
+ /// <param name="start">Index to start filling at.</param>
+ /// <param name="len">Length of segment to fill.</param>
+ public virtual void NextBytes(
+ byte[] bytes,
+ int start,
+ int len)
+ {
+ doNextBytes(bytes, start, len);
+ }
+
+ private void doNextBytes(
+ byte[] bytes,
+ int start,
+ int len)
+ {
+ lock (this)
+ {
+ int done = 0;
+ while (done < len)
+ {
+ if (windowCount < 1)
+ {
+ generator.NextBytes(window, 0, window.Length);
+ windowCount = window.Length;
+ }
+
+ bytes[start + done++] = window[--windowCount];
+ }
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/prng/ThreadedSeedGenerator.cs b/Crypto/src/crypto/prng/ThreadedSeedGenerator.cs
new file mode 100644
index 000000000..9f918ea6e
--- /dev/null
+++ b/Crypto/src/crypto/prng/ThreadedSeedGenerator.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Threading;
+
+namespace Org.BouncyCastle.Crypto.Prng
+{
+ /**
+ * A thread based seed generator - one source of randomness.
+ * <p>
+ * Based on an idea from Marcus Lippert.
+ * </p>
+ */
+ public class ThreadedSeedGenerator
+ {
+ private class SeedGenerator
+ {
+#if NETCF_1_0
+ // No volatile keyword, but all fields implicitly volatile anyway
+ private int counter = 0;
+ private bool stop = false;
+#else
+ private volatile int counter = 0;
+ private volatile bool stop = false;
+#endif
+
+ private void Run(object ignored)
+ {
+ while (!this.stop)
+ {
+ this.counter++;
+ }
+ }
+
+ public byte[] GenerateSeed(
+ int numBytes,
+ bool fast)
+ {
+ this.counter = 0;
+ this.stop = false;
+
+ byte[] result = new byte[numBytes];
+ int last = 0;
+ int end = fast ? numBytes : numBytes * 8;
+
+ ThreadPool.QueueUserWorkItem(new WaitCallback(Run));
+
+ for (int i = 0; i < end; i++)
+ {
+ var waitEvent = new ManualResetEvent(false);
+
+ while (this.counter == last)
+ {
+ try
+ {
+ waitEvent.WaitOne(1);
+ }
+ catch (Exception)
+ {
+ // ignore
+ }
+ }
+
+ last = this.counter;
+
+ if (fast)
+ {
+ result[i] = (byte) last;
+ }
+ else
+ {
+ int bytepos = i / 8;
+ result[bytepos] = (byte) ((result[bytepos] << 1) | (last & 1));
+ }
+ }
+
+ this.stop = true;
+
+ return result;
+ }
+ }
+
+ /**
+ * Generate seed bytes. Set fast to false for best quality.
+ * <p>
+ * If fast is set to true, the code should be round about 8 times faster when
+ * generating a long sequence of random bytes. 20 bytes of random values using
+ * the fast mode take less than half a second on a Nokia e70. If fast is set to false,
+ * it takes round about 2500 ms.
+ * </p>
+ * @param numBytes the number of bytes to generate
+ * @param fast true if fast mode should be used
+ */
+ public byte[] GenerateSeed(
+ int numBytes,
+ bool fast)
+ {
+ return new SeedGenerator().GenerateSeed(numBytes, fast);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/prng/VMPCRandomGenerator.cs b/Crypto/src/crypto/prng/VMPCRandomGenerator.cs
new file mode 100644
index 000000000..2ab079999
--- /dev/null
+++ b/Crypto/src/crypto/prng/VMPCRandomGenerator.cs
@@ -0,0 +1,115 @@
+namespace Org.BouncyCastle.Crypto.Prng
+{
+ public class VmpcRandomGenerator
+ : IRandomGenerator
+ {
+ private byte n = 0;
+
+ /// <remarks>
+ /// Permutation generated by code:
+ /// <code>
+ /// // First 1850 fractional digit of Pi number.
+ /// byte[] key = new BigInteger("14159265358979323846...5068006422512520511").ToByteArray();
+ /// s = 0;
+ /// P = new byte[256];
+ /// for (int i = 0; i < 256; i++)
+ /// {
+ /// P[i] = (byte) i;
+ /// }
+ /// for (int m = 0; m < 768; m++)
+ /// {
+ /// s = P[(s + P[m & 0xff] + key[m % key.length]) & 0xff];
+ /// byte temp = P[m & 0xff];
+ /// P[m & 0xff] = P[s & 0xff];
+ /// P[s & 0xff] = temp;
+ /// } </code>
+ /// </remarks>
+ private byte[] P =
+ {
+ (byte) 0xbb, (byte) 0x2c, (byte) 0x62, (byte) 0x7f, (byte) 0xb5, (byte) 0xaa, (byte) 0xd4,
+ (byte) 0x0d, (byte) 0x81, (byte) 0xfe, (byte) 0xb2, (byte) 0x82, (byte) 0xcb, (byte) 0xa0, (byte) 0xa1,
+ (byte) 0x08, (byte) 0x18, (byte) 0x71, (byte) 0x56, (byte) 0xe8, (byte) 0x49, (byte) 0x02, (byte) 0x10,
+ (byte) 0xc4, (byte) 0xde, (byte) 0x35, (byte) 0xa5, (byte) 0xec, (byte) 0x80, (byte) 0x12, (byte) 0xb8,
+ (byte) 0x69, (byte) 0xda, (byte) 0x2f, (byte) 0x75, (byte) 0xcc, (byte) 0xa2, (byte) 0x09, (byte) 0x36,
+ (byte) 0x03, (byte) 0x61, (byte) 0x2d, (byte) 0xfd, (byte) 0xe0, (byte) 0xdd, (byte) 0x05, (byte) 0x43,
+ (byte) 0x90, (byte) 0xad, (byte) 0xc8, (byte) 0xe1, (byte) 0xaf, (byte) 0x57, (byte) 0x9b, (byte) 0x4c,
+ (byte) 0xd8, (byte) 0x51, (byte) 0xae, (byte) 0x50, (byte) 0x85, (byte) 0x3c, (byte) 0x0a, (byte) 0xe4,
+ (byte) 0xf3, (byte) 0x9c, (byte) 0x26, (byte) 0x23, (byte) 0x53, (byte) 0xc9, (byte) 0x83, (byte) 0x97,
+ (byte) 0x46, (byte) 0xb1, (byte) 0x99, (byte) 0x64, (byte) 0x31, (byte) 0x77, (byte) 0xd5, (byte) 0x1d,
+ (byte) 0xd6, (byte) 0x78, (byte) 0xbd, (byte) 0x5e, (byte) 0xb0, (byte) 0x8a, (byte) 0x22, (byte) 0x38,
+ (byte) 0xf8, (byte) 0x68, (byte) 0x2b, (byte) 0x2a, (byte) 0xc5, (byte) 0xd3, (byte) 0xf7, (byte) 0xbc,
+ (byte) 0x6f, (byte) 0xdf, (byte) 0x04, (byte) 0xe5, (byte) 0x95, (byte) 0x3e, (byte) 0x25, (byte) 0x86,
+ (byte) 0xa6, (byte) 0x0b, (byte) 0x8f, (byte) 0xf1, (byte) 0x24, (byte) 0x0e, (byte) 0xd7, (byte) 0x40,
+ (byte) 0xb3, (byte) 0xcf, (byte) 0x7e, (byte) 0x06, (byte) 0x15, (byte) 0x9a, (byte) 0x4d, (byte) 0x1c,
+ (byte) 0xa3, (byte) 0xdb, (byte) 0x32, (byte) 0x92, (byte) 0x58, (byte) 0x11, (byte) 0x27, (byte) 0xf4,
+ (byte) 0x59, (byte) 0xd0, (byte) 0x4e, (byte) 0x6a, (byte) 0x17, (byte) 0x5b, (byte) 0xac, (byte) 0xff,
+ (byte) 0x07, (byte) 0xc0, (byte) 0x65, (byte) 0x79, (byte) 0xfc, (byte) 0xc7, (byte) 0xcd, (byte) 0x76,
+ (byte) 0x42, (byte) 0x5d, (byte) 0xe7, (byte) 0x3a, (byte) 0x34, (byte) 0x7a, (byte) 0x30, (byte) 0x28,
+ (byte) 0x0f, (byte) 0x73, (byte) 0x01, (byte) 0xf9, (byte) 0xd1, (byte) 0xd2, (byte) 0x19, (byte) 0xe9,
+ (byte) 0x91, (byte) 0xb9, (byte) 0x5a, (byte) 0xed, (byte) 0x41, (byte) 0x6d, (byte) 0xb4, (byte) 0xc3,
+ (byte) 0x9e, (byte) 0xbf, (byte) 0x63, (byte) 0xfa, (byte) 0x1f, (byte) 0x33, (byte) 0x60, (byte) 0x47,
+ (byte) 0x89, (byte) 0xf0, (byte) 0x96, (byte) 0x1a, (byte) 0x5f, (byte) 0x93, (byte) 0x3d, (byte) 0x37,
+ (byte) 0x4b, (byte) 0xd9, (byte) 0xa8, (byte) 0xc1, (byte) 0x1b, (byte) 0xf6, (byte) 0x39, (byte) 0x8b,
+ (byte) 0xb7, (byte) 0x0c, (byte) 0x20, (byte) 0xce, (byte) 0x88, (byte) 0x6e, (byte) 0xb6, (byte) 0x74,
+ (byte) 0x8e, (byte) 0x8d, (byte) 0x16, (byte) 0x29, (byte) 0xf2, (byte) 0x87, (byte) 0xf5, (byte) 0xeb,
+ (byte) 0x70, (byte) 0xe3, (byte) 0xfb, (byte) 0x55, (byte) 0x9f, (byte) 0xc6, (byte) 0x44, (byte) 0x4a,
+ (byte) 0x45, (byte) 0x7d, (byte) 0xe2, (byte) 0x6b, (byte) 0x5c, (byte) 0x6c, (byte) 0x66, (byte) 0xa9,
+ (byte) 0x8c, (byte) 0xee, (byte) 0x84, (byte) 0x13, (byte) 0xa7, (byte) 0x1e, (byte) 0x9d, (byte) 0xdc,
+ (byte) 0x67, (byte) 0x48, (byte) 0xba, (byte) 0x2e, (byte) 0xe6, (byte) 0xa4, (byte) 0xab, (byte) 0x7c,
+ (byte) 0x94, (byte) 0x00, (byte) 0x21, (byte) 0xef, (byte) 0xea, (byte) 0xbe, (byte) 0xca, (byte) 0x72,
+ (byte) 0x4f, (byte) 0x52, (byte) 0x98, (byte) 0x3f, (byte) 0xc2, (byte) 0x14, (byte) 0x7b, (byte) 0x3b,
+ (byte) 0x54
+ };
+
+ /// <remarks>Value generated in the same way as <c>P</c>.</remarks>
+ private byte s = (byte) 0xbe;
+
+ public VmpcRandomGenerator()
+ {
+ }
+
+ public virtual void AddSeedMaterial(byte[] seed)
+ {
+ for (int m = 0; m < seed.Length; m++)
+ {
+ s = P[(s + P[n & 0xff] + seed[m]) & 0xff];
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+ }
+
+ public virtual void AddSeedMaterial(long seed)
+ {
+ byte[] s = new byte[4];
+ s[3] = (byte) (seed & 0x000000ff);
+ s[2] = (byte) ((seed & 0x0000ff00) >> 8);
+ s[1] = (byte) ((seed & 0x00ff0000) >> 16);
+ s[0] = (byte) ((seed & 0xff000000) >> 24);
+ AddSeedMaterial(s);
+ }
+
+ public virtual void NextBytes(byte[] bytes)
+ {
+ NextBytes(bytes, 0, bytes.Length);
+ }
+
+ public virtual void NextBytes(byte[] bytes, int start, int len)
+ {
+ lock (P)
+ {
+ int end = start + len;
+ for (int i = start; i != end; i++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ bytes[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/DsaDigestSigner.cs b/Crypto/src/crypto/signers/DsaDigestSigner.cs
new file mode 100644
index 000000000..aee713450
--- /dev/null
+++ b/Crypto/src/crypto/signers/DsaDigestSigner.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ public class DsaDigestSigner
+ : ISigner
+ {
+ private readonly IDigest digest;
+ private readonly IDsa dsaSigner;
+ private bool forSigning;
+
+ public DsaDigestSigner(
+ IDsa signer,
+ IDigest digest)
+ {
+ this.digest = digest;
+ this.dsaSigner = signer;
+ }
+
+ public string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; }
+ }
+
+ public void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ this.forSigning = forSigning;
+
+ AsymmetricKeyParameter k;
+
+ if (parameters is ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)parameters;
+ }
+
+ if (forSigning && !k.IsPrivate)
+ throw new InvalidKeyException("Signing Requires Private Key.");
+
+ if (!forSigning && k.IsPrivate)
+ throw new InvalidKeyException("Verification Requires Public Key.");
+
+ Reset();
+
+ dsaSigner.Init(forSigning, parameters);
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void Update(
+ byte input)
+ {
+ digest.Update(input);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ digest.BlockUpdate(input, inOff, length);
+ }
+
+ /**
+ * Generate a signature for the message we've been loaded with using
+ * the key we were initialised with.
+ */
+ public byte[] GenerateSignature()
+ {
+ if (!forSigning)
+ throw new InvalidOperationException("DSADigestSigner not initialised for signature generation.");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ BigInteger[] sig = dsaSigner.GenerateSignature(hash);
+
+ return DerEncode(sig[0], sig[1]);
+ }
+
+ /// <returns>true if the internal state represents the signature described in the passed in array.</returns>
+ public bool VerifySignature(
+ byte[] signature)
+ {
+ if (forSigning)
+ throw new InvalidOperationException("DSADigestSigner not initialised for verification");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ try
+ {
+ BigInteger[] sig = DerDecode(signature);
+ return dsaSigner.VerifySignature(hash, sig[0], sig[1]);
+ }
+ catch (IOException)
+ {
+ return false;
+ }
+ }
+
+ /// <summary>Reset the internal state</summary>
+ public void Reset()
+ {
+ digest.Reset();
+ }
+
+ private byte[] DerEncode(
+ BigInteger r,
+ BigInteger s)
+ {
+ return new DerSequence(new DerInteger(r), new DerInteger(s)).GetDerEncoded();
+ }
+
+ private BigInteger[] DerDecode(
+ byte[] encoding)
+ {
+ Asn1Sequence s = (Asn1Sequence) Asn1Object.FromByteArray(encoding);
+
+ return new BigInteger[]
+ {
+ ((DerInteger) s[0]).Value,
+ ((DerInteger) s[1]).Value
+ };
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/DsaSigner.cs b/Crypto/src/crypto/signers/DsaSigner.cs
new file mode 100644
index 000000000..419b1972e
--- /dev/null
+++ b/Crypto/src/crypto/signers/DsaSigner.cs
@@ -0,0 +1,136 @@
+using System;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /**
+ * The Digital Signature Algorithm - as described in "Handbook of Applied
+ * Cryptography", pages 452 - 453.
+ */
+ public class DsaSigner
+ : IDsa
+ {
+ private DsaKeyParameters key;
+ private SecureRandom random;
+
+ public string AlgorithmName
+ {
+ get { return "DSA"; }
+ }
+
+ public void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ if (forSigning)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+
+ this.random = rParam.Random;
+ parameters = rParam.Parameters;
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ if (!(parameters is DsaPrivateKeyParameters))
+ throw new InvalidKeyException("DSA private key required for signing");
+
+ this.key = (DsaPrivateKeyParameters) parameters;
+ }
+ else
+ {
+ if (!(parameters is DsaPublicKeyParameters))
+ throw new InvalidKeyException("DSA public key required for verification");
+
+ this.key = (DsaPublicKeyParameters) parameters;
+ }
+ }
+
+ /**
+ * Generate a signature for the given message using the key we were
+ * initialised with. For conventional DSA the message should be a SHA-1
+ * hash of the message of interest.
+ *
+ * @param message the message that will be verified later.
+ */
+ public BigInteger[] GenerateSignature(
+ byte[] message)
+ {
+ DsaParameters parameters = key.Parameters;
+ BigInteger q = parameters.Q;
+ BigInteger m = calculateE(q, message);
+ BigInteger k;
+
+ do
+ {
+ k = new BigInteger(q.BitLength, random);
+ }
+ while (k.CompareTo(q) >= 0);
+
+ BigInteger r = parameters.G.ModPow(k, parameters.P).Mod(q);
+
+ k = k.ModInverse(q).Multiply(
+ m.Add(((DsaPrivateKeyParameters)key).X.Multiply(r)));
+
+ BigInteger s = k.Mod(q);
+
+ return new BigInteger[]{ r, s };
+ }
+
+ /**
+ * return true if the value r and s represent a DSA signature for
+ * the passed in message for standard DSA the message should be a
+ * SHA-1 hash of the real message to be verified.
+ */
+ public bool VerifySignature(
+ byte[] message,
+ BigInteger r,
+ BigInteger s)
+ {
+ DsaParameters parameters = key.Parameters;
+ BigInteger q = parameters.Q;
+ BigInteger m = calculateE(q, message);
+
+ if (r.SignValue <= 0 || q.CompareTo(r) <= 0)
+ {
+ return false;
+ }
+
+ if (s.SignValue <= 0 || q.CompareTo(s) <= 0)
+ {
+ return false;
+ }
+
+ BigInteger w = s.ModInverse(q);
+
+ BigInteger u1 = m.Multiply(w).Mod(q);
+ BigInteger u2 = r.Multiply(w).Mod(q);
+
+ BigInteger p = parameters.P;
+ u1 = parameters.G.ModPow(u1, p);
+ u2 = ((DsaPublicKeyParameters)key).Y.ModPow(u2, p);
+
+ BigInteger v = u1.Multiply(u2).Mod(p).Mod(q);
+
+ return v.Equals(r);
+ }
+
+ private BigInteger calculateE(
+ BigInteger n,
+ byte[] message)
+ {
+ int length = System.Math.Min(message.Length, n.BitLength / 8);
+
+ return new BigInteger(1, message, 0, length);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/ECDsaSigner.cs b/Crypto/src/crypto/signers/ECDsaSigner.cs
new file mode 100644
index 000000000..4254e5590
--- /dev/null
+++ b/Crypto/src/crypto/signers/ECDsaSigner.cs
@@ -0,0 +1,156 @@
+using System;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /**
+ * EC-DSA as described in X9.62
+ */
+ public class ECDsaSigner
+ : IDsa
+ {
+ private ECKeyParameters key;
+ private SecureRandom random;
+
+ public string AlgorithmName
+ {
+ get { return "ECDSA"; }
+ }
+
+ public void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ if (forSigning)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom) parameters;
+
+ this.random = rParam.Random;
+ parameters = rParam.Parameters;
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ if (!(parameters is ECPrivateKeyParameters))
+ throw new InvalidKeyException("EC private key required for signing");
+
+ this.key = (ECPrivateKeyParameters) parameters;
+ }
+ else
+ {
+ if (!(parameters is ECPublicKeyParameters))
+ throw new InvalidKeyException("EC public key required for verification");
+
+ this.key = (ECPublicKeyParameters) parameters;
+ }
+ }
+
+ // 5.3 pg 28
+ /**
+ * Generate a signature for the given message using the key we were
+ * initialised with. For conventional DSA the message should be a SHA-1
+ * hash of the message of interest.
+ *
+ * @param message the message that will be verified later.
+ */
+ public BigInteger[] GenerateSignature(
+ byte[] message)
+ {
+ BigInteger n = key.Parameters.N;
+ BigInteger e = calculateE(n, message);
+
+ BigInteger r = null;
+ BigInteger s = null;
+
+ // 5.3.2
+ do // Generate s
+ {
+ BigInteger k = null;
+
+ do // Generate r
+ {
+ do
+ {
+ k = new BigInteger(n.BitLength, random);
+ }
+ while (k.SignValue == 0 || k.CompareTo(n) >= 0);
+
+ ECPoint p = key.Parameters.G.Multiply(k);
+
+ // 5.3.3
+ BigInteger x = p.X.ToBigInteger();
+
+ r = x.Mod(n);
+ }
+ while (r.SignValue == 0);
+
+ BigInteger d = ((ECPrivateKeyParameters)key).D;
+
+ s = k.ModInverse(n).Multiply(e.Add(d.Multiply(r).Mod(n))).Mod(n);
+ }
+ while (s.SignValue == 0);
+
+ return new BigInteger[]{ r, s };
+ }
+
+ // 5.4 pg 29
+ /**
+ * return true if the value r and s represent a DSA signature for
+ * the passed in message (for standard DSA the message should be
+ * a SHA-1 hash of the real message to be verified).
+ */
+ public bool VerifySignature(
+ byte[] message,
+ BigInteger r,
+ BigInteger s)
+ {
+ BigInteger n = key.Parameters.N;
+
+ // r and s should both in the range [1,n-1]
+ if (r.SignValue < 1 || s.SignValue < 1
+ || r.CompareTo(n) >= 0 || s.CompareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ BigInteger e = calculateE(n, message);
+ BigInteger c = s.ModInverse(n);
+
+ BigInteger u1 = e.Multiply(c).Mod(n);
+ BigInteger u2 = r.Multiply(c).Mod(n);
+
+ ECPoint G = key.Parameters.G;
+ ECPoint Q = ((ECPublicKeyParameters) key).Q;
+
+ ECPoint point = ECAlgorithms.SumOfTwoMultiplies(G, u1, Q, u2);
+
+ BigInteger v = point.X.ToBigInteger().Mod(n);
+
+ return v.Equals(r);
+ }
+
+ private BigInteger calculateE(
+ BigInteger n,
+ byte[] message)
+ {
+ int messageBitLength = message.Length * 8;
+ BigInteger trunc = new BigInteger(1, message);
+
+ if (n.BitLength < messageBitLength)
+ {
+ trunc = trunc.ShiftRight(messageBitLength - n.BitLength);
+ }
+
+ return trunc;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/ECGOST3410Signer.cs b/Crypto/src/crypto/signers/ECGOST3410Signer.cs
new file mode 100644
index 000000000..d68b83f67
--- /dev/null
+++ b/Crypto/src/crypto/signers/ECGOST3410Signer.cs
@@ -0,0 +1,154 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /**
+ * GOST R 34.10-2001 Signature Algorithm
+ */
+ public class ECGost3410Signer
+ : IDsa
+ {
+ private ECKeyParameters key;
+ private SecureRandom random;
+
+ public string AlgorithmName
+ {
+ get { return "ECGOST3410"; }
+ }
+
+ public void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ if (forSigning)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+
+ this.random = rParam.Random;
+ parameters = rParam.Parameters;
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ if (!(parameters is ECPrivateKeyParameters))
+ throw new InvalidKeyException("EC private key required for signing");
+
+ this.key = (ECPrivateKeyParameters) parameters;
+ }
+ else
+ {
+ if (!(parameters is ECPublicKeyParameters))
+ throw new InvalidKeyException("EC public key required for verification");
+
+ this.key = (ECPublicKeyParameters)parameters;
+ }
+ }
+
+ /**
+ * generate a signature for the given message using the key we were
+ * initialised with. For conventional GOST3410 the message should be a GOST3411
+ * hash of the message of interest.
+ *
+ * @param message the message that will be verified later.
+ */
+ public BigInteger[] GenerateSignature(
+ byte[] message)
+ {
+ byte[] mRev = new byte[message.Length]; // conversion is little-endian
+ for (int i = 0; i != mRev.Length; i++)
+ {
+ mRev[i] = message[mRev.Length - 1 - i];
+ }
+
+ BigInteger e = new BigInteger(1, mRev);
+ BigInteger n = key.Parameters.N;
+
+ BigInteger r = null;
+ BigInteger s = null;
+
+ do // generate s
+ {
+ BigInteger k = null;
+
+ do // generate r
+ {
+ do
+ {
+ k = new BigInteger(n.BitLength, random);
+ }
+ while (k.SignValue == 0);
+
+ ECPoint p = key.Parameters.G.Multiply(k);
+
+ BigInteger x = p.X.ToBigInteger();
+
+ r = x.Mod(n);
+ }
+ while (r.SignValue == 0);
+
+ BigInteger d = ((ECPrivateKeyParameters)key).D;
+
+ s = (k.Multiply(e)).Add(d.Multiply(r)).Mod(n);
+ }
+ while (s.SignValue == 0);
+
+ return new BigInteger[]{ r, s };
+ }
+
+ /**
+ * return true if the value r and s represent a GOST3410 signature for
+ * the passed in message (for standard GOST3410 the message should be
+ * a GOST3411 hash of the real message to be verified).
+ */
+ public bool VerifySignature(
+ byte[] message,
+ BigInteger r,
+ BigInteger s)
+ {
+ byte[] mRev = new byte[message.Length]; // conversion is little-endian
+ for (int i = 0; i != mRev.Length; i++)
+ {
+ mRev[i] = message[mRev.Length - 1 - i];
+ }
+
+ BigInteger e = new BigInteger(1, mRev);
+ BigInteger n = key.Parameters.N;
+
+ // r in the range [1,n-1]
+ if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ // s in the range [1,n-1]
+ if (s.CompareTo(BigInteger.One) < 0 || s.CompareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ BigInteger v = e.ModInverse(n);
+
+ BigInteger z1 = s.Multiply(v).Mod(n);
+ BigInteger z2 = (n.Subtract(r)).Multiply(v).Mod(n);
+
+ ECPoint G = key.Parameters.G; // P
+ ECPoint Q = ((ECPublicKeyParameters)key).Q;
+
+ ECPoint point = ECAlgorithms.SumOfTwoMultiplies(G, z1, Q, z2);
+
+ BigInteger R = point.X.ToBigInteger().Mod(n);
+
+ return R.Equals(r);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/ECNRSigner.cs b/Crypto/src/crypto/signers/ECNRSigner.cs
new file mode 100644
index 000000000..63865d731
--- /dev/null
+++ b/Crypto/src/crypto/signers/ECNRSigner.cs
@@ -0,0 +1,186 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /**
+ * EC-NR as described in IEEE 1363-2000
+ */
+ public class ECNRSigner
+ : IDsa
+ {
+ private bool forSigning;
+ private ECKeyParameters key;
+ private SecureRandom random;
+
+ public string AlgorithmName
+ {
+ get { return "ECNR"; }
+ }
+
+ public void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ this.forSigning = forSigning;
+
+ if (forSigning)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom) parameters;
+
+ this.random = rParam.Random;
+ parameters = rParam.Parameters;
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ if (!(parameters is ECPrivateKeyParameters))
+ throw new InvalidKeyException("EC private key required for signing");
+
+ this.key = (ECPrivateKeyParameters) parameters;
+ }
+ else
+ {
+ if (!(parameters is ECPublicKeyParameters))
+ throw new InvalidKeyException("EC public key required for verification");
+
+ this.key = (ECPublicKeyParameters) parameters;
+ }
+ }
+
+ // Section 7.2.5 ECSP-NR, pg 34
+ /**
+ * generate a signature for the given message using the key we were
+ * initialised with. Generally, the order of the curve should be at
+ * least as long as the hash of the message of interest, and with
+ * ECNR it *must* be at least as long.
+ *
+ * @param digest the digest to be signed.
+ * @exception DataLengthException if the digest is longer than the key allows
+ */
+ public BigInteger[] GenerateSignature(
+ byte[] message)
+ {
+ if (!this.forSigning)
+ {
+ // not properly initilaized... deal with it
+ throw new InvalidOperationException("not initialised for signing");
+ }
+
+ BigInteger n = ((ECPrivateKeyParameters) this.key).Parameters.N;
+ int nBitLength = n.BitLength;
+
+ BigInteger e = new BigInteger(1, message);
+ int eBitLength = e.BitLength;
+
+ ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)key;
+
+ if (eBitLength > nBitLength)
+ {
+ throw new DataLengthException("input too large for ECNR key.");
+ }
+
+ BigInteger r = null;
+ BigInteger s = null;
+
+ AsymmetricCipherKeyPair tempPair;
+ do // generate r
+ {
+ // generate another, but very temporary, key pair using
+ // the same EC parameters
+ ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
+
+ keyGen.Init(new ECKeyGenerationParameters(privKey.Parameters, this.random));
+
+ tempPair = keyGen.GenerateKeyPair();
+
+ // BigInteger Vx = tempPair.getPublic().getW().getAffineX();
+ ECPublicKeyParameters V = (ECPublicKeyParameters) tempPair.Public; // get temp's public key
+ BigInteger Vx = V.Q.X.ToBigInteger(); // get the point's x coordinate
+
+ r = Vx.Add(e).Mod(n);
+ }
+ while (r.SignValue == 0);
+
+ // generate s
+ BigInteger x = privKey.D; // private key value
+ BigInteger u = ((ECPrivateKeyParameters) tempPair.Private).D; // temp's private key value
+ s = u.Subtract(r.Multiply(x)).Mod(n);
+
+ return new BigInteger[]{ r, s };
+ }
+
+ // Section 7.2.6 ECVP-NR, pg 35
+ /**
+ * return true if the value r and s represent a signature for the
+ * message passed in. Generally, the order of the curve should be at
+ * least as long as the hash of the message of interest, and with
+ * ECNR, it *must* be at least as long. But just in case the signer
+ * applied mod(n) to the longer digest, this implementation will
+ * apply mod(n) during verification.
+ *
+ * @param digest the digest to be verified.
+ * @param r the r value of the signature.
+ * @param s the s value of the signature.
+ * @exception DataLengthException if the digest is longer than the key allows
+ */
+ public bool VerifySignature(
+ byte[] message,
+ BigInteger r,
+ BigInteger s)
+ {
+ if (this.forSigning)
+ {
+ // not properly initilaized... deal with it
+ throw new InvalidOperationException("not initialised for verifying");
+ }
+
+ ECPublicKeyParameters pubKey = (ECPublicKeyParameters)key;
+ BigInteger n = pubKey.Parameters.N;
+ int nBitLength = n.BitLength;
+
+ BigInteger e = new BigInteger(1, message);
+ int eBitLength = e.BitLength;
+
+ if (eBitLength > nBitLength)
+ {
+ throw new DataLengthException("input too large for ECNR key.");
+ }
+
+ // r in the range [1,n-1]
+ if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ // TODO So why is this different from the spec?
+ // s in the range [0,n-1] NB: ECNR spec says 0
+ if (s.CompareTo(BigInteger.Zero) < 0 || s.CompareTo(n) >= 0)
+ {
+ return false;
+ }
+
+ // compute P = sG + rW
+
+ ECPoint G = pubKey.Parameters.G;
+ ECPoint W = pubKey.Q;
+ // calculate P using Bouncy math
+ ECPoint P = ECAlgorithms.SumOfTwoMultiplies(G, s, W, r);
+
+ BigInteger x = P.X.ToBigInteger();
+ BigInteger t = r.Subtract(x).Mod(n);
+
+ return t.Equals(e);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/GOST3410DigestSigner.cs b/Crypto/src/crypto/signers/GOST3410DigestSigner.cs
new file mode 100644
index 000000000..58aefa368
--- /dev/null
+++ b/Crypto/src/crypto/signers/GOST3410DigestSigner.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ public class Gost3410DigestSigner
+ : ISigner
+ {
+ private readonly IDigest digest;
+ private readonly IDsa dsaSigner;
+ private bool forSigning;
+
+ public Gost3410DigestSigner(
+ IDsa signer,
+ IDigest digest)
+ {
+ this.dsaSigner = signer;
+ this.digest = digest;
+ }
+
+ public string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; }
+ }
+
+ public void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ this.forSigning = forSigning;
+
+ AsymmetricKeyParameter k;
+ if (parameters is ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)parameters;
+ }
+
+ if (forSigning && !k.IsPrivate)
+ {
+ throw new InvalidKeyException("Signing Requires Private Key.");
+ }
+
+ if (!forSigning && k.IsPrivate)
+ {
+ throw new InvalidKeyException("Verification Requires Public Key.");
+ }
+
+ Reset();
+
+ dsaSigner.Init(forSigning, parameters);
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void Update(
+ byte input)
+ {
+ digest.Update(input);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ digest.BlockUpdate(input, inOff, length);
+ }
+
+ /**
+ * Generate a signature for the message we've been loaded with using
+ * the key we were initialised with.
+ */
+ public byte[] GenerateSignature()
+ {
+ if (!forSigning)
+ throw new InvalidOperationException("GOST3410DigestSigner not initialised for signature generation.");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ try
+ {
+ BigInteger[] sig = dsaSigner.GenerateSignature(hash);
+ byte[] sigBytes = new byte[64];
+
+ // TODO Add methods to allow writing BigInteger to existing byte array?
+ byte[] r = sig[0].ToByteArrayUnsigned();
+ byte[] s = sig[1].ToByteArrayUnsigned();
+ s.CopyTo(sigBytes, 32 - s.Length);
+ r.CopyTo(sigBytes, 64 - r.Length);
+ return sigBytes;
+ }
+ catch (Exception e)
+ {
+ throw new SignatureException(e.Message, e);
+ }
+ }
+
+ /// <returns>true if the internal state represents the signature described in the passed in array.</returns>
+ public bool VerifySignature(
+ byte[] signature)
+ {
+ if (forSigning)
+ throw new InvalidOperationException("DSADigestSigner not initialised for verification");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ BigInteger R, S;
+ try
+ {
+ R = new BigInteger(1, signature, 32, 32);
+ S = new BigInteger(1, signature, 0, 32);
+ }
+ catch (Exception e)
+ {
+ throw new SignatureException("error decoding signature bytes.", e);
+ }
+
+ return dsaSigner.VerifySignature(hash, R, S);
+ }
+
+ /// <summary>Reset the internal state</summary>
+ public void Reset()
+ {
+ digest.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/GOST3410Signer.cs b/Crypto/src/crypto/signers/GOST3410Signer.cs
new file mode 100644
index 000000000..375eeb5cc
--- /dev/null
+++ b/Crypto/src/crypto/signers/GOST3410Signer.cs
@@ -0,0 +1,132 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /**
+ * Gost R 34.10-94 Signature Algorithm
+ */
+ public class Gost3410Signer
+ : IDsa
+ {
+ private Gost3410KeyParameters key;
+ private SecureRandom random;
+
+ public string AlgorithmName
+ {
+ get { return "GOST3410"; }
+ }
+
+ public void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ if (forSigning)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+
+ this.random = rParam.Random;
+ parameters = rParam.Parameters;
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ if (!(parameters is Gost3410PrivateKeyParameters))
+ throw new InvalidKeyException("GOST3410 private key required for signing");
+
+ this.key = (Gost3410PrivateKeyParameters) parameters;
+ }
+ else
+ {
+ if (!(parameters is Gost3410PublicKeyParameters))
+ throw new InvalidKeyException("GOST3410 public key required for signing");
+
+ this.key = (Gost3410PublicKeyParameters) parameters;
+ }
+ }
+
+ /**
+ * generate a signature for the given message using the key we were
+ * initialised with. For conventional Gost3410 the message should be a Gost3411
+ * hash of the message of interest.
+ *
+ * @param message the message that will be verified later.
+ */
+ public BigInteger[] GenerateSignature(
+ byte[] message)
+ {
+ byte[] mRev = new byte[message.Length]; // conversion is little-endian
+ for (int i = 0; i != mRev.Length; i++)
+ {
+ mRev[i] = message[mRev.Length - 1 - i];
+ }
+
+ BigInteger m = new BigInteger(1, mRev);
+ Gost3410Parameters parameters = key.Parameters;
+ BigInteger k;
+
+ do
+ {
+ k = new BigInteger(parameters.Q.BitLength, random);
+ }
+ while (k.CompareTo(parameters.Q) >= 0);
+
+ BigInteger r = parameters.A.ModPow(k, parameters.P).Mod(parameters.Q);
+
+ BigInteger s = k.Multiply(m).
+ Add(((Gost3410PrivateKeyParameters)key).X.Multiply(r)).
+ Mod(parameters.Q);
+
+ return new BigInteger[]{ r, s };
+ }
+
+ /**
+ * return true if the value r and s represent a Gost3410 signature for
+ * the passed in message for standard Gost3410 the message should be a
+ * Gost3411 hash of the real message to be verified.
+ */
+ public bool VerifySignature(
+ byte[] message,
+ BigInteger r,
+ BigInteger s)
+ {
+ byte[] mRev = new byte[message.Length]; // conversion is little-endian
+ for (int i = 0; i != mRev.Length; i++)
+ {
+ mRev[i] = message[mRev.Length - 1 - i];
+ }
+
+ BigInteger m = new BigInteger(1, mRev);
+ Gost3410Parameters parameters = key.Parameters;
+
+ if (r.SignValue < 0 || parameters.Q.CompareTo(r) <= 0)
+ {
+ return false;
+ }
+
+ if (s.SignValue < 0 || parameters.Q.CompareTo(s) <= 0)
+ {
+ return false;
+ }
+
+ BigInteger v = m.ModPow(parameters.Q.Subtract(BigInteger.Two), parameters.Q);
+
+ BigInteger z1 = s.Multiply(v).Mod(parameters.Q);
+ BigInteger z2 = (parameters.Q.Subtract(r)).Multiply(v).Mod(parameters.Q);
+
+ z1 = parameters.A.ModPow(z1, parameters.P);
+ z2 = ((Gost3410PublicKeyParameters)key).Y.ModPow(z2, parameters.P);
+
+ BigInteger u = z1.Multiply(z2).Mod(parameters.P).Mod(parameters.Q);
+
+ return u.Equals(r);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/GenericSigner.cs b/Crypto/src/crypto/signers/GenericSigner.cs
new file mode 100644
index 000000000..1a53eee2b
--- /dev/null
+++ b/Crypto/src/crypto/signers/GenericSigner.cs
@@ -0,0 +1,129 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ public class GenericSigner
+ : ISigner
+ {
+ private readonly IAsymmetricBlockCipher engine;
+ private readonly IDigest digest;
+ private bool forSigning;
+
+ public GenericSigner(
+ IAsymmetricBlockCipher engine,
+ IDigest digest)
+ {
+ this.engine = engine;
+ this.digest = digest;
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Generic(" + engine.AlgorithmName + "/" + digest.AlgorithmName + ")"; }
+ }
+
+ /**
+ * initialise the signer for signing or verification.
+ *
+ * @param forSigning
+ * true if for signing, false otherwise
+ * @param parameters
+ * necessary parameters.
+ */
+ public void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ this.forSigning = forSigning;
+ AsymmetricKeyParameter k;
+
+ if (parameters is ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)parameters;
+ }
+
+ if (forSigning && !k.IsPrivate)
+ throw new InvalidKeyException("Signing requires private key.");
+
+ if (!forSigning && k.IsPrivate)
+ throw new InvalidKeyException("Verification requires public key.");
+
+ Reset();
+
+ engine.Init(forSigning, parameters);
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void Update(
+ byte input)
+ {
+ digest.Update(input);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ digest.BlockUpdate(input, inOff, length);
+ }
+
+ /**
+ * Generate a signature for the message we've been loaded with using the key
+ * we were initialised with.
+ */
+ public byte[] GenerateSignature()
+ {
+ if (!forSigning)
+ throw new InvalidOperationException("GenericSigner not initialised for signature generation.");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ return engine.ProcessBlock(hash, 0, hash.Length);
+ }
+
+ /**
+ * return true if the internal state represents the signature described in
+ * the passed in array.
+ */
+ public bool VerifySignature(
+ byte[] signature)
+ {
+ if (forSigning)
+ throw new InvalidOperationException("GenericSigner not initialised for verification");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ try
+ {
+ byte[] sig = engine.ProcessBlock(signature, 0, signature.Length);
+
+ return Arrays.ConstantTimeAreEqual(sig, hash);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ public void Reset()
+ {
+ digest.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/Crypto/src/crypto/signers/Iso9796d2PssSigner.cs
new file mode 100644
index 000000000..48cd719e9
--- /dev/null
+++ b/Crypto/src/crypto/signers/Iso9796d2PssSigner.cs
@@ -0,0 +1,576 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /// <summary> ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3).
+ /// <p>
+ /// Note: the usual length for the salt is the length of the hash
+ /// function used in bytes.</p>
+ /// </summary>
+ public class Iso9796d2PssSigner
+ : ISignerWithRecovery
+ {
+ /// <summary>
+ /// Return a reference to the recoveredMessage message.
+ /// </summary>
+ /// <returns>The full/partial recoveredMessage message.</returns>
+ /// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/>
+ public byte[] GetRecoveredMessage()
+ {
+ return recoveredMessage;
+ }
+
+ public const int TrailerImplicit = 0xBC;
+ public const int TrailerRipeMD160 = 0x31CC;
+ public const int TrailerRipeMD128 = 0x32CC;
+ public const int TrailerSha1 = 0x33CC;
+
+ private IDigest digest;
+ private IAsymmetricBlockCipher cipher;
+
+ private SecureRandom random;
+ private byte[] standardSalt;
+
+ private int hLen;
+ private int trailer;
+ private int keyBits;
+ private byte[] block;
+ private byte[] mBuf;
+ private int messageLength;
+ private readonly int saltLength;
+ private bool fullMessage;
+ private byte[] recoveredMessage;
+
+ /// <summary>
+ /// Generate a signer for the with either implicit or explicit trailers
+ /// for ISO9796-2, scheme 2 or 3.
+ /// </summary>
+ /// <param name="cipher">base cipher to use for signature creation/verification</param>
+ /// <param name="digest">digest to use.</param>
+ /// <param name="saltLength">length of salt in bytes.</param>
+ /// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param>
+ public Iso9796d2PssSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest digest,
+ int saltLength,
+ bool isImplicit)
+ {
+ this.cipher = cipher;
+ this.digest = digest;
+ this.hLen = digest.GetDigestSize();
+ this.saltLength = saltLength;
+
+ if (isImplicit)
+ {
+ trailer = TrailerImplicit;
+ }
+ else
+ {
+ if (digest is Sha1Digest)
+ {
+ trailer = TrailerSha1;
+ }
+ else if (digest is RipeMD160Digest)
+ {
+ trailer = TrailerRipeMD160;
+ }
+ else if (digest is RipeMD128Digest)
+ {
+ trailer = TrailerRipeMD128;
+ }
+ else
+ {
+ throw new ArgumentException("no valid trailer for digest");
+ }
+ }
+ }
+
+ /// <summary> Constructor for a signer with an explicit digest trailer.
+ ///
+ /// </summary>
+ /// <param name="cipher">cipher to use.
+ /// </param>
+ /// <param name="digest">digest to sign with.
+ /// </param>
+ /// <param name="saltLength">length of salt in bytes.
+ /// </param>
+ public Iso9796d2PssSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest digest,
+ int saltLength)
+ : this(cipher, digest, saltLength, false)
+ {
+ }
+
+ public string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "with" + "ISO9796-2S2"; }
+ }
+
+ /// <summary>Initialise the signer.</summary>
+ /// <param name="forSigning">true if for signing, false if for verification.</param>
+ /// <param name="parameters">parameters for signature generation/verification. If the
+ /// parameters are for generation they should be a ParametersWithRandom,
+ /// a ParametersWithSalt, or just an RsaKeyParameters object. If RsaKeyParameters
+ /// are passed in a SecureRandom will be created.
+ /// </param>
+ /// <exception cref="ArgumentException">if wrong parameter type or a fixed
+ /// salt is passed in which is the wrong length.
+ /// </exception>
+ public virtual void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ RsaKeyParameters kParam;
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom) parameters;
+
+ kParam = (RsaKeyParameters) p.Parameters;
+
+ if (forSigning)
+ {
+ random = p.Random;
+ }
+ }
+ else if (parameters is ParametersWithSalt)
+ {
+ if (!forSigning)
+ throw new ArgumentException("ParametersWithSalt only valid for signing", "parameters");
+
+ ParametersWithSalt p = (ParametersWithSalt) parameters;
+
+ kParam = (RsaKeyParameters) p.Parameters;
+ standardSalt = p.GetSalt();
+
+ if (standardSalt.Length != saltLength)
+ throw new ArgumentException("Fixed salt is of wrong length");
+ }
+ else
+ {
+ kParam = (RsaKeyParameters) parameters;
+
+ if (forSigning)
+ {
+ random = new SecureRandom();
+ }
+ }
+
+ cipher.Init(forSigning, kParam);
+
+ keyBits = kParam.Modulus.BitLength;
+
+ block = new byte[(keyBits + 7) / 8];
+
+ if (trailer == TrailerImplicit)
+ {
+ mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 1];
+ }
+ else
+ {
+ mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 2];
+ }
+
+ Reset();
+ }
+
+ /// <summary> compare two byte arrays - constant time.</summary>
+ private bool IsSameAs(byte[] a, byte[] b)
+ {
+ if (messageLength != b.Length)
+ {
+ return false;
+ }
+
+ bool isOkay = true;
+
+ for (int i = 0; i != b.Length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ isOkay = false;
+ }
+ }
+
+ return isOkay;
+ }
+
+ /// <summary> clear possible sensitive data</summary>
+ private void ClearBlock(
+ byte[] block)
+ {
+ Array.Clear(block, 0, block.Length);
+ }
+
+ public virtual void UpdateWithRecoveredMessage(
+ byte[] signature)
+ {
+ // TODO
+ throw Platform.CreateNotImplementedException("UpdateWithRecoveredMessage");
+ }
+
+ /// <summary> update the internal digest with the byte b</summary>
+ public virtual void Update(
+ byte input)
+ {
+ if (messageLength < mBuf.Length)
+ {
+ mBuf[messageLength++] = input;
+ }
+ else
+ {
+ digest.Update(input);
+ }
+ }
+
+ /// <summary> update the internal digest with the byte array in</summary>
+ public virtual void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ while (length > 0 && messageLength < mBuf.Length)
+ {
+ this.Update(input[inOff]);
+ inOff++;
+ length--;
+ }
+
+ if (length > 0)
+ {
+ digest.BlockUpdate(input, inOff, length);
+ }
+ }
+
+ /// <summary> reset the internal state</summary>
+ public virtual void Reset()
+ {
+ digest.Reset();
+ messageLength = 0;
+ if (mBuf != null)
+ {
+ ClearBlock(mBuf);
+ }
+ if (recoveredMessage != null)
+ {
+ ClearBlock(recoveredMessage);
+ recoveredMessage = null;
+ }
+ fullMessage = false;
+ }
+
+ /// <summary> Generate a signature for the loaded message using the key we were
+ /// initialised with.
+ /// </summary>
+ public byte[] GenerateSignature()
+ {
+ int digSize = digest.GetDigestSize();
+ byte[] m2Hash = new byte[digSize];
+ digest.DoFinal(m2Hash, 0);
+
+ byte[] C = new byte[8];
+ LtoOSP(messageLength * 8, C);
+
+ digest.BlockUpdate(C, 0, C.Length);
+ digest.BlockUpdate(mBuf, 0, messageLength);
+ digest.BlockUpdate(m2Hash, 0, m2Hash.Length);
+
+ byte[] salt;
+ if (standardSalt != null)
+ {
+ salt = standardSalt;
+ }
+ else
+ {
+ salt = new byte[saltLength];
+ random.NextBytes(salt);
+ }
+
+ digest.BlockUpdate(salt, 0, salt.Length);
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ int tLength = 2;
+ if (trailer == TrailerImplicit)
+ {
+ tLength = 1;
+ }
+
+ int off = block.Length - messageLength - salt.Length - hLen - tLength - 1;
+
+ block[off] = (byte) (0x01);
+
+ Array.Copy(mBuf, 0, block, off + 1, messageLength);
+ Array.Copy(salt, 0, block, off + 1 + messageLength, salt.Length);
+
+ byte[] dbMask = MaskGeneratorFunction1(hash, 0, hash.Length, block.Length - hLen - tLength);
+ for (int i = 0; i != dbMask.Length; i++)
+ {
+ block[i] ^= dbMask[i];
+ }
+
+ Array.Copy(hash, 0, block, block.Length - hLen - tLength, hLen);
+
+ if (trailer == TrailerImplicit)
+ {
+ block[block.Length - 1] = (byte)TrailerImplicit;
+ }
+ else
+ {
+ block[block.Length - 2] = (byte) ((uint)trailer >> 8);
+ block[block.Length - 1] = (byte) trailer;
+ }
+
+ block[0] &= (byte) (0x7f);
+
+ byte[] b = cipher.ProcessBlock(block, 0, block.Length);
+
+ ClearBlock(mBuf);
+ ClearBlock(block);
+ messageLength = 0;
+
+ return b;
+ }
+
+ /// <summary> return true if the signature represents a ISO9796-2 signature
+ /// for the passed in message.
+ /// </summary>
+ public virtual bool VerifySignature(
+ byte[] signature)
+ {
+ byte[] block = cipher.ProcessBlock(signature, 0, signature.Length);
+
+ //
+ // adjust block size for leading zeroes if necessary
+ //
+ int expectedSize = (keyBits + 7) / 8;
+ if (block.Length < expectedSize)
+ {
+ byte[] tmp = new byte[expectedSize];
+ block.CopyTo(tmp, tmp.Length - block.Length);
+ ClearBlock(block);
+ block = tmp;
+ }
+
+ int tLength;
+
+ if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
+ {
+ tLength = 1;
+ }
+ else
+ {
+ int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
+
+ switch (sigTrail)
+ {
+ case TrailerRipeMD160:
+ if (!(digest is RipeMD160Digest))
+ {
+ throw new ArgumentException("signer should be initialised with RipeMD160");
+ }
+ break;
+ case TrailerSha1:
+ if (!(digest is Sha1Digest))
+ {
+ throw new ArgumentException("signer should be initialised with SHA1");
+ }
+ break;
+ case TrailerRipeMD128:
+ if (!(digest is RipeMD128Digest))
+ {
+ throw new ArgumentException("signer should be initialised with RipeMD128");
+ }
+ break;
+ default:
+ throw new ArgumentException("unrecognised hash in signature");
+ }
+
+ tLength = 2;
+ }
+
+ //
+ // calculate H(m2)
+ //
+ byte[] m2Hash = new byte[hLen];
+ digest.DoFinal(m2Hash, 0);
+
+ //
+ // remove the mask
+ //
+ byte[] dbMask = MaskGeneratorFunction1(block, block.Length - hLen - tLength, hLen, block.Length - hLen - tLength);
+ for (int i = 0; i != dbMask.Length; i++)
+ {
+ block[i] ^= dbMask[i];
+ }
+
+ block[0] &= 0x7f;
+
+ //
+ // find out how much padding we've got
+ //
+ int mStart = 0;
+ while (mStart < block.Length)
+ {
+ if (block[mStart++] == 0x01)
+ break;
+ }
+
+ if (mStart >= block.Length)
+ {
+ ClearBlock(block);
+ return false;
+ }
+
+ fullMessage = (mStart > 1);
+
+ // TODO Should we check if a standardSalt was set and, if so, use its length instead?
+ recoveredMessage = new byte[dbMask.Length - mStart - saltLength];
+
+ Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+
+ //
+ // check the hashes
+ //
+ byte[] C = new byte[8];
+ LtoOSP(recoveredMessage.Length * 8, C);
+
+ digest.BlockUpdate(C, 0, C.Length);
+
+ if (recoveredMessage.Length != 0)
+ {
+ digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length);
+ }
+
+ digest.BlockUpdate(m2Hash, 0, m2Hash.Length);
+
+ // Update for the salt
+ digest.BlockUpdate(block, mStart + recoveredMessage.Length, saltLength);
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ int off = block.Length - tLength - hash.Length;
+
+ // TODO ConstantTimeAreEqual with offset for one array
+
+ bool isOkay = true;
+ for (int i = 0; i != hash.Length; i++)
+ {
+ if (hash[i] != block[off + i])
+ {
+ isOkay = false;
+ }
+ }
+
+ ClearBlock(block);
+ ClearBlock(hash);
+
+ if (!isOkay)
+ {
+ fullMessage = false;
+ ClearBlock(recoveredMessage);
+ return false;
+ }
+
+ //
+ // if they've input a message check what we've recovered against
+ // what was input.
+ //
+ if (messageLength != 0)
+ {
+ if (!IsSameAs(mBuf, recoveredMessage))
+ {
+ ClearBlock(mBuf);
+ return false;
+ }
+
+ messageLength = 0;
+ }
+
+ ClearBlock(mBuf);
+ return true;
+ }
+
+ /// <summary>
+ /// Return true if the full message was recoveredMessage.
+ /// </summary>
+ /// <returns>true on full message recovery, false otherwise, or if not sure.</returns>
+ /// <seealso cref="ISignerWithRecovery.HasFullMessage"/>
+ public virtual bool HasFullMessage()
+ {
+ return fullMessage;
+ }
+
+ /// <summary> int to octet string.</summary>
+ /// <summary> int to octet string.</summary>
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)((uint)i >> 24);
+ sp[1] = (byte)((uint)i >> 16);
+ sp[2] = (byte)((uint)i >> 8);
+ sp[3] = (byte)((uint)i >> 0);
+ }
+
+ /// <summary> long to octet string.</summary>
+ private void LtoOSP(long l, byte[] sp)
+ {
+ sp[0] = (byte)((ulong)l >> 56);
+ sp[1] = (byte)((ulong)l >> 48);
+ sp[2] = (byte)((ulong)l >> 40);
+ sp[3] = (byte)((ulong)l >> 32);
+ sp[4] = (byte)((ulong)l >> 24);
+ sp[5] = (byte)((ulong)l >> 16);
+ sp[6] = (byte)((ulong)l >> 8);
+ sp[7] = (byte)((ulong)l >> 0);
+ }
+
+ /// <summary> mask generator function, as described in Pkcs1v2.</summary>
+ private byte[] MaskGeneratorFunction1(
+ byte[] Z,
+ int zOff,
+ int zLen,
+ int length)
+ {
+ byte[] mask = new byte[length];
+ byte[] hashBuf = new byte[hLen];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ digest.Reset();
+
+ do
+ {
+ ItoOSP(counter, C);
+
+ digest.BlockUpdate(Z, zOff, zLen);
+ digest.BlockUpdate(C, 0, C.Length);
+ digest.DoFinal(hashBuf, 0);
+
+ Array.Copy(hashBuf, 0, mask, counter * hLen, hLen);
+ }
+ while (++counter < (length / hLen));
+
+ if ((counter * hLen) < length)
+ {
+ ItoOSP(counter, C);
+
+ digest.BlockUpdate(Z, zOff, zLen);
+ digest.BlockUpdate(C, 0, C.Length);
+ digest.DoFinal(hashBuf, 0);
+
+ Array.Copy(hashBuf, 0, mask, counter * hLen, mask.Length - (counter * hLen));
+ }
+
+ return mask;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/Iso9796d2Signer.cs b/Crypto/src/crypto/signers/Iso9796d2Signer.cs
new file mode 100644
index 000000000..8ff87e8ee
--- /dev/null
+++ b/Crypto/src/crypto/signers/Iso9796d2Signer.cs
@@ -0,0 +1,557 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /// <summary> ISO9796-2 - mechanism using a hash function with recovery (scheme 1)</summary>
+ public class Iso9796d2Signer : ISignerWithRecovery
+ {
+ /// <summary>
+ /// Return a reference to the recoveredMessage message.
+ /// </summary>
+ /// <returns>The full/partial recoveredMessage message.</returns>
+ /// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/>
+ public byte[] GetRecoveredMessage()
+ {
+ return recoveredMessage;
+ }
+
+ public const int TrailerImplicit = 0xBC;
+ public const int TrailerRipeMD160 = 0x31CC;
+ public const int TrailerRipeMD128 = 0x32CC;
+ public const int TrailerSha1 = 0x33CC;
+ public const int TrailerSha256 = 0x34CC;
+ public const int TrailerSha512 = 0x35CC;
+ public const int TrailerSha384 = 0x36CC;
+ public const int TrailerWhirlpool = 0x37CC;
+
+ private static IDictionary trailerMap = Platform.CreateHashtable();
+
+ static Iso9796d2Signer()
+ {
+ trailerMap.Add("RIPEMD128", TrailerRipeMD128);
+ trailerMap.Add("RIPEMD160", TrailerRipeMD160);
+
+ trailerMap.Add("SHA-1", TrailerSha1);
+ trailerMap.Add("SHA-256", TrailerSha256);
+ trailerMap.Add("SHA-384", TrailerSha384);
+ trailerMap.Add("SHA-512", TrailerSha512);
+
+ trailerMap.Add("Whirlpool", TrailerWhirlpool);
+ }
+
+ private IDigest digest;
+ private IAsymmetricBlockCipher cipher;
+
+ private int trailer;
+ private int keyBits;
+ private byte[] block;
+ private byte[] mBuf;
+ private int messageLength;
+ private bool fullMessage;
+ private byte[] recoveredMessage;
+
+ private byte[] preSig;
+ private byte[] preBlock;
+
+ /// <summary>
+ /// Generate a signer for the with either implicit or explicit trailers
+ /// for ISO9796-2.
+ /// </summary>
+ /// <param name="cipher">base cipher to use for signature creation/verification</param>
+ /// <param name="digest">digest to use.</param>
+ /// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param>
+ public Iso9796d2Signer(
+ IAsymmetricBlockCipher cipher,
+ IDigest digest,
+ bool isImplicit)
+ {
+ this.cipher = cipher;
+ this.digest = digest;
+
+ if (isImplicit)
+ {
+ trailer = TrailerImplicit;
+ }
+ else
+ {
+ string digestName = digest.AlgorithmName;
+
+ if (trailerMap.Contains(digestName))
+ {
+ trailer = (int)trailerMap[digest.AlgorithmName];
+ }
+ else
+ {
+ throw new System.ArgumentException("no valid trailer for digest");
+ }
+ }
+ }
+
+ /// <summary> Constructor for a signer with an explicit digest trailer.
+ ///
+ /// </summary>
+ /// <param name="cipher">cipher to use.
+ /// </param>
+ /// <param name="digest">digest to sign with.
+ /// </param>
+ public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest)
+ : this(cipher, digest, false)
+ {
+ }
+
+ public string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; }
+ }
+
+ public virtual void Init(bool forSigning, ICipherParameters parameters)
+ {
+ RsaKeyParameters kParam = (RsaKeyParameters) parameters;
+
+ cipher.Init(forSigning, kParam);
+
+ keyBits = kParam.Modulus.BitLength;
+
+ block = new byte[(keyBits + 7) / 8];
+ if (trailer == TrailerImplicit)
+ {
+ mBuf = new byte[block.Length - digest.GetDigestSize() - 2];
+ }
+ else
+ {
+ mBuf = new byte[block.Length - digest.GetDigestSize() - 3];
+ }
+
+ Reset();
+ }
+
+ /// <summary> compare two byte arrays - constant time.</summary>
+ private bool IsSameAs(byte[] a, byte[] b)
+ {
+ int checkLen;
+ if (messageLength > mBuf.Length)
+ {
+ if (mBuf.Length > b.Length)
+ {
+ return false;
+ }
+
+ checkLen = mBuf.Length;
+ }
+ else
+ {
+ if (messageLength != b.Length)
+ {
+ return false;
+ }
+
+ checkLen = b.Length;
+ }
+
+ bool isOkay = true;
+
+ for (int i = 0; i != checkLen; i++)
+ {
+ if (a[i] != b[i])
+ {
+ isOkay = false;
+ }
+ }
+
+ return isOkay;
+ }
+
+ /// <summary> clear possible sensitive data</summary>
+ private void ClearBlock(
+ byte[] block)
+ {
+ Array.Clear(block, 0, block.Length);
+ }
+
+ public virtual void UpdateWithRecoveredMessage(
+ byte[] signature)
+ {
+ byte[] block = cipher.ProcessBlock(signature, 0, signature.Length);
+
+ if (((block[0] & 0xC0) ^ 0x40) != 0)
+ throw new InvalidCipherTextException("malformed signature");
+
+ if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0)
+ throw new InvalidCipherTextException("malformed signature");
+
+ int delta = 0;
+
+ if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
+ {
+ delta = 1;
+ }
+ else
+ {
+ int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
+
+ string digestName = digest.AlgorithmName;
+ if (!trailerMap.Contains(digestName))
+ throw new ArgumentException("unrecognised hash in signature");
+ if (sigTrail != (int)trailerMap[digestName])
+ throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail);
+
+ delta = 2;
+ }
+
+ //
+ // find out how much padding we've got
+ //
+ int mStart = 0;
+
+ for (mStart = 0; mStart != block.Length; mStart++)
+ {
+ if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
+ break;
+ }
+
+ mStart++;
+
+ int off = block.Length - delta - digest.GetDigestSize();
+
+ //
+ // there must be at least one byte of message string
+ //
+ if ((off - mStart) <= 0)
+ throw new InvalidCipherTextException("malformed block");
+
+ //
+ // if we contain the whole message as well, check the hash of that.
+ //
+ if ((block[0] & 0x20) == 0)
+ {
+ fullMessage = true;
+
+ recoveredMessage = new byte[off - mStart];
+ Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+ }
+ else
+ {
+ fullMessage = false;
+
+ recoveredMessage = new byte[off - mStart];
+ Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+ }
+
+ preSig = signature;
+ preBlock = block;
+
+ digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length);
+ messageLength = recoveredMessage.Length;
+ }
+
+ /// <summary> update the internal digest with the byte b</summary>
+ public void Update(
+ byte input)
+ {
+ digest.Update(input);
+
+ if (preSig == null && messageLength < mBuf.Length)
+ {
+ mBuf[messageLength] = input;
+ }
+
+ messageLength++;
+ }
+
+ /// <summary> update the internal digest with the byte array in</summary>
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ digest.BlockUpdate(input, inOff, length);
+
+ if (preSig == null && messageLength < mBuf.Length)
+ {
+ for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++)
+ {
+ mBuf[messageLength + i] = input[inOff + i];
+ }
+ }
+
+ messageLength += length;
+ }
+
+ /// <summary> reset the internal state</summary>
+ public virtual void Reset()
+ {
+ digest.Reset();
+ messageLength = 0;
+ ClearBlock(mBuf);
+
+ if (recoveredMessage != null)
+ {
+ ClearBlock(recoveredMessage);
+ }
+
+ recoveredMessage = null;
+ fullMessage = false;
+ }
+
+ /// <summary> Generate a signature for the loaded message using the key we were
+ /// initialised with.
+ /// </summary>
+ public virtual byte[] GenerateSignature()
+ {
+ int digSize = digest.GetDigestSize();
+
+ int t = 0;
+ int delta = 0;
+
+ if (trailer == TrailerImplicit)
+ {
+ t = 8;
+ delta = block.Length - digSize - 1;
+ digest.DoFinal(block, delta);
+ block[block.Length - 1] = (byte) TrailerImplicit;
+ }
+ else
+ {
+ t = 16;
+ delta = block.Length - digSize - 2;
+ digest.DoFinal(block, delta);
+ block[block.Length - 2] = (byte) ((uint)trailer >> 8);
+ block[block.Length - 1] = (byte) trailer;
+ }
+
+ byte header = 0;
+ int x = (digSize + messageLength) * 8 + t + 4 - keyBits;
+
+ if (x > 0)
+ {
+ int mR = messageLength - ((x + 7) / 8);
+ header = (byte) (0x60);
+
+ delta -= mR;
+
+ Array.Copy(mBuf, 0, block, delta, mR);
+ }
+ else
+ {
+ header = (byte) (0x40);
+ delta -= messageLength;
+
+ Array.Copy(mBuf, 0, block, delta, messageLength);
+ }
+
+ if ((delta - 1) > 0)
+ {
+ for (int i = delta - 1; i != 0; i--)
+ {
+ block[i] = (byte) 0xbb;
+ }
+ block[delta - 1] ^= (byte) 0x01;
+ block[0] = (byte) 0x0b;
+ block[0] |= header;
+ }
+ else
+ {
+ block[0] = (byte) 0x0a;
+ block[0] |= header;
+ }
+
+ byte[] b = cipher.ProcessBlock(block, 0, block.Length);
+
+ ClearBlock(mBuf);
+ ClearBlock(block);
+
+ return b;
+ }
+
+ /// <summary> return true if the signature represents a ISO9796-2 signature
+ /// for the passed in message.
+ /// </summary>
+ public virtual bool VerifySignature(byte[] signature)
+ {
+ byte[] block;
+ bool updateWithRecoveredCalled;
+
+ if (preSig == null)
+ {
+ updateWithRecoveredCalled = false;
+ try
+ {
+ block = cipher.ProcessBlock(signature, 0, signature.Length);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!Arrays.AreEqual(preSig, signature))
+ throw new InvalidOperationException("updateWithRecoveredMessage called on different signature");
+
+ updateWithRecoveredCalled = true;
+ block = preBlock;
+
+ preSig = null;
+ preBlock = null;
+ }
+
+ if (((block[0] & 0xC0) ^ 0x40) != 0)
+ return ReturnFalse(block);
+
+ if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0)
+ return ReturnFalse(block);
+
+ int delta = 0;
+
+ if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
+ {
+ delta = 1;
+ }
+ else
+ {
+ int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
+
+ string digestName = digest.AlgorithmName;
+ if (!trailerMap.Contains(digestName))
+ throw new ArgumentException("unrecognised hash in signature");
+ if (sigTrail != (int)trailerMap[digestName])
+ throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail);
+
+ delta = 2;
+ }
+
+ //
+ // find out how much padding we've got
+ //
+ int mStart = 0;
+ for (; mStart != block.Length; mStart++)
+ {
+ if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
+ {
+ break;
+ }
+ }
+
+ mStart++;
+
+ //
+ // check the hashes
+ //
+ byte[] hash = new byte[digest.GetDigestSize()];
+
+ int off = block.Length - delta - hash.Length;
+
+ //
+ // there must be at least one byte of message string
+ //
+ if ((off - mStart) <= 0)
+ {
+ return ReturnFalse(block);
+ }
+
+ //
+ // if we contain the whole message as well, check the hash of that.
+ //
+ if ((block[0] & 0x20) == 0)
+ {
+ fullMessage = true;
+
+ // check right number of bytes passed in.
+ if (messageLength > off - mStart)
+ {
+ return ReturnFalse(block);
+ }
+
+ digest.Reset();
+ digest.BlockUpdate(block, mStart, off - mStart);
+ digest.DoFinal(hash, 0);
+
+ bool isOkay = true;
+
+ for (int i = 0; i != hash.Length; i++)
+ {
+ block[off + i] ^= hash[i];
+ if (block[off + i] != 0)
+ {
+ isOkay = false;
+ }
+ }
+
+ if (!isOkay)
+ {
+ return ReturnFalse(block);
+ }
+
+ recoveredMessage = new byte[off - mStart];
+ Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+ }
+ else
+ {
+ fullMessage = false;
+
+ digest.DoFinal(hash, 0);
+
+ bool isOkay = true;
+
+ for (int i = 0; i != hash.Length; i++)
+ {
+ block[off + i] ^= hash[i];
+ if (block[off + i] != 0)
+ {
+ isOkay = false;
+ }
+ }
+
+ if (!isOkay)
+ {
+ return ReturnFalse(block);
+ }
+
+ recoveredMessage = new byte[off - mStart];
+ Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+ }
+
+ //
+ // if they've input a message check what we've recovered against
+ // what was input.
+ //
+ if (messageLength != 0 && !updateWithRecoveredCalled)
+ {
+ if (!IsSameAs(mBuf, recoveredMessage))
+ {
+// ClearBlock(recoveredMessage);
+ return ReturnFalse(block);
+ }
+ }
+
+ ClearBlock(mBuf);
+ ClearBlock(block);
+
+ return true;
+ }
+
+ private bool ReturnFalse(byte[] block)
+ {
+ ClearBlock(mBuf);
+ ClearBlock(block);
+
+ return false;
+ }
+
+ /// <summary>
+ /// Return true if the full message was recoveredMessage.
+ /// </summary>
+ /// <returns> true on full message recovery, false otherwise.</returns>
+ /// <seealso cref="ISignerWithRecovery.HasFullMessage"/>
+ public virtual bool HasFullMessage()
+ {
+ return fullMessage;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/PssSigner.cs b/Crypto/src/crypto/signers/PssSigner.cs
new file mode 100644
index 000000000..6900224f3
--- /dev/null
+++ b/Crypto/src/crypto/signers/PssSigner.cs
@@ -0,0 +1,345 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /// <summary> RSA-PSS as described in Pkcs# 1 v 2.1.
+ /// <p>
+ /// Note: the usual value for the salt length is the number of
+ /// bytes in the hash function.</p>
+ /// </summary>
+ public class PssSigner
+ : ISigner
+ {
+ public const byte TrailerImplicit = (byte)0xBC;
+
+ private readonly IDigest contentDigest1, contentDigest2;
+ private readonly IDigest mgfDigest;
+ private readonly IAsymmetricBlockCipher cipher;
+
+ private SecureRandom random;
+
+ private int hLen;
+ private int mgfhLen;
+ private int sLen;
+ private int emBits;
+ private byte[] salt;
+ private byte[] mDash;
+ private byte[] block;
+ private byte trailer;
+
+ public static PssSigner CreateRawSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest digest)
+ {
+ return new PssSigner(cipher, new NullDigest(), digest, digest, digest.GetDigestSize(), TrailerImplicit);
+ }
+
+ public static PssSigner CreateRawSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest contentDigest,
+ IDigest mgfDigest,
+ int saltLen,
+ byte trailer)
+ {
+ return new PssSigner(cipher, new NullDigest(), contentDigest, mgfDigest, saltLen, trailer);
+ }
+
+ public PssSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest digest)
+ : this(cipher, digest, digest.GetDigestSize())
+ {
+ }
+
+ /// <summary>Basic constructor</summary>
+ /// <param name="cipher">the asymmetric cipher to use.</param>
+ /// <param name="digest">the digest to use.</param>
+ /// <param name="saltLen">the length of the salt to use (in bytes).</param>
+ public PssSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest digest,
+ int saltLen)
+ : this(cipher, digest, saltLen, TrailerImplicit)
+ {
+ }
+
+ public PssSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest contentDigest,
+ IDigest mgfDigest,
+ int saltLen)
+ : this(cipher, contentDigest, mgfDigest, saltLen, TrailerImplicit)
+ {
+ }
+
+ public PssSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest digest,
+ int saltLen,
+ byte trailer)
+ : this(cipher, digest, digest, saltLen, TrailerImplicit)
+ {
+ }
+
+ public PssSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest contentDigest,
+ IDigest mgfDigest,
+ int saltLen,
+ byte trailer)
+ : this(cipher, contentDigest, contentDigest, mgfDigest, saltLen, trailer)
+ {
+ }
+
+ private PssSigner(
+ IAsymmetricBlockCipher cipher,
+ IDigest contentDigest1,
+ IDigest contentDigest2,
+ IDigest mgfDigest,
+ int saltLen,
+ byte trailer)
+ {
+ this.cipher = cipher;
+ this.contentDigest1 = contentDigest1;
+ this.contentDigest2 = contentDigest2;
+ this.mgfDigest = mgfDigest;
+ this.hLen = contentDigest2.GetDigestSize();
+ this.mgfhLen = mgfDigest.GetDigestSize();
+ this.sLen = saltLen;
+ this.salt = new byte[saltLen];
+ this.mDash = new byte[8 + saltLen + hLen];
+ this.trailer = trailer;
+ }
+
+ public string AlgorithmName
+ {
+ get { return mgfDigest.AlgorithmName + "withRSAandMGF1"; }
+ }
+
+ public virtual void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom) parameters;
+
+ parameters = p.Parameters;
+ random = p.Random;
+ }
+ else
+ {
+ if (forSigning)
+ {
+ random = new SecureRandom();
+ }
+ }
+
+ cipher.Init(forSigning, parameters);
+
+ RsaKeyParameters kParam;
+ if (parameters is RsaBlindingParameters)
+ {
+ kParam = ((RsaBlindingParameters) parameters).PublicKey;
+ }
+ else
+ {
+ kParam = (RsaKeyParameters) parameters;
+ }
+
+ emBits = kParam.Modulus.BitLength - 1;
+
+ if (emBits < (8 * hLen + 8 * sLen + 9))
+ throw new ArgumentException("key too small for specified hash and salt lengths");
+
+ block = new byte[(emBits + 7) / 8];
+ }
+
+ /// <summary> clear possible sensitive data</summary>
+ private void ClearBlock(
+ byte[] block)
+ {
+ Array.Clear(block, 0, block.Length);
+ }
+
+ /// <summary> update the internal digest with the byte b</summary>
+ public virtual void Update(
+ byte input)
+ {
+ contentDigest1.Update(input);
+ }
+
+ /// <summary> update the internal digest with the byte array in</summary>
+ public virtual void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ contentDigest1.BlockUpdate(input, inOff, length);
+ }
+
+ /// <summary> reset the internal state</summary>
+ public virtual void Reset()
+ {
+ contentDigest1.Reset();
+ }
+
+ /// <summary> Generate a signature for the message we've been loaded with using
+ /// the key we were initialised with.
+ /// </summary>
+ public virtual byte[] GenerateSignature()
+ {
+ contentDigest1.DoFinal(mDash, mDash.Length - hLen - sLen);
+
+ if (sLen != 0)
+ {
+ random.NextBytes(salt);
+ salt.CopyTo(mDash, mDash.Length - sLen);
+ }
+
+ byte[] h = new byte[hLen];
+
+ contentDigest2.BlockUpdate(mDash, 0, mDash.Length);
+
+ contentDigest2.DoFinal(h, 0);
+
+ block[block.Length - sLen - 1 - hLen - 1] = (byte) (0x01);
+ salt.CopyTo(block, block.Length - sLen - hLen - 1);
+
+ byte[] dbMask = MaskGeneratorFunction1(h, 0, h.Length, block.Length - hLen - 1);
+ for (int i = 0; i != dbMask.Length; i++)
+ {
+ block[i] ^= dbMask[i];
+ }
+
+ block[0] &= (byte) ((0xff >> ((block.Length * 8) - emBits)));
+
+ h.CopyTo(block, block.Length - hLen - 1);
+
+ block[block.Length - 1] = trailer;
+
+ byte[] b = cipher.ProcessBlock(block, 0, block.Length);
+
+ ClearBlock(block);
+
+ return b;
+ }
+
+ /// <summary> return true if the internal state represents the signature described
+ /// in the passed in array.
+ /// </summary>
+ public virtual bool VerifySignature(
+ byte[] signature)
+ {
+ contentDigest1.DoFinal(mDash, mDash.Length - hLen - sLen);
+
+ byte[] b = cipher.ProcessBlock(signature, 0, signature.Length);
+ b.CopyTo(block, block.Length - b.Length);
+
+ if (block[block.Length - 1] != trailer)
+ {
+ ClearBlock(block);
+ return false;
+ }
+
+ byte[] dbMask = MaskGeneratorFunction1(block, block.Length - hLen - 1, hLen, block.Length - hLen - 1);
+
+ for (int i = 0; i != dbMask.Length; i++)
+ {
+ block[i] ^= dbMask[i];
+ }
+
+ block[0] &= (byte) ((0xff >> ((block.Length * 8) - emBits)));
+
+ for (int i = 0; i != block.Length - hLen - sLen - 2; i++)
+ {
+ if (block[i] != 0)
+ {
+ ClearBlock(block);
+ return false;
+ }
+ }
+
+ if (block[block.Length - hLen - sLen - 2] != 0x01)
+ {
+ ClearBlock(block);
+ return false;
+ }
+
+ Array.Copy(block, block.Length - sLen - hLen - 1, mDash, mDash.Length - sLen, sLen);
+
+ contentDigest2.BlockUpdate(mDash, 0, mDash.Length);
+ contentDigest2.DoFinal(mDash, mDash.Length - hLen);
+
+ for (int i = block.Length - hLen - 1, j = mDash.Length - hLen; j != mDash.Length; i++, j++)
+ {
+ if ((block[i] ^ mDash[j]) != 0)
+ {
+ ClearBlock(mDash);
+ ClearBlock(block);
+ return false;
+ }
+ }
+
+ ClearBlock(mDash);
+ ClearBlock(block);
+
+ return true;
+ }
+
+ /// <summary> int to octet string.</summary>
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)((uint) i >> 24);
+ sp[1] = (byte)((uint) i >> 16);
+ sp[2] = (byte)((uint) i >> 8);
+ sp[3] = (byte)((uint) i >> 0);
+ }
+
+ /// <summary> mask generator function, as described in Pkcs1v2.</summary>
+ private byte[] MaskGeneratorFunction1(
+ byte[] Z,
+ int zOff,
+ int zLen,
+ int length)
+ {
+ byte[] mask = new byte[length];
+ byte[] hashBuf = new byte[mgfhLen];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ mgfDigest.Reset();
+
+ while (counter < (length / mgfhLen))
+ {
+ ItoOSP(counter, C);
+
+ mgfDigest.BlockUpdate(Z, zOff, zLen);
+ mgfDigest.BlockUpdate(C, 0, C.Length);
+ mgfDigest.DoFinal(hashBuf, 0);
+
+ hashBuf.CopyTo(mask, counter * mgfhLen);
+ ++counter;
+ }
+
+ if ((counter * mgfhLen) < length)
+ {
+ ItoOSP(counter, C);
+
+ mgfDigest.BlockUpdate(Z, zOff, zLen);
+ mgfDigest.BlockUpdate(C, 0, C.Length);
+ mgfDigest.DoFinal(hashBuf, 0);
+
+ Array.Copy(hashBuf, 0, mask, counter * mgfhLen, mask.Length - (counter * mgfhLen));
+ }
+
+ return mask;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/signers/RsaDigestSigner.cs b/Crypto/src/crypto/signers/RsaDigestSigner.cs
new file mode 100644
index 000000000..f57bfc83d
--- /dev/null
+++ b/Crypto/src/crypto/signers/RsaDigestSigner.cs
@@ -0,0 +1,228 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ public class RsaDigestSigner
+ : ISigner
+ {
+ private readonly IAsymmetricBlockCipher rsaEngine = new Pkcs1Encoding(new RsaBlindedEngine());
+ private readonly AlgorithmIdentifier algId;
+ private readonly IDigest digest;
+ private bool forSigning;
+
+ private static readonly IDictionary oidMap = Platform.CreateHashtable();
+
+ /// <summary>
+ /// Load oid table.
+ /// </summary>
+ static RsaDigestSigner()
+ {
+ oidMap["RIPEMD128"] = TeleTrusTObjectIdentifiers.RipeMD128;
+ oidMap["RIPEMD160"] = TeleTrusTObjectIdentifiers.RipeMD160;
+ oidMap["RIPEMD256"] = TeleTrusTObjectIdentifiers.RipeMD256;
+
+ oidMap["SHA-1"] = X509ObjectIdentifiers.IdSha1;
+ oidMap["SHA-224"] = NistObjectIdentifiers.IdSha224;
+ oidMap["SHA-256"] = NistObjectIdentifiers.IdSha256;
+ oidMap["SHA-384"] = NistObjectIdentifiers.IdSha384;
+ oidMap["SHA-512"] = NistObjectIdentifiers.IdSha512;
+
+ oidMap["MD2"] = PkcsObjectIdentifiers.MD2;
+ oidMap["MD4"] = PkcsObjectIdentifiers.MD4;
+ oidMap["MD5"] = PkcsObjectIdentifiers.MD5;
+ }
+
+ public RsaDigestSigner(
+ IDigest digest)
+ {
+ this.digest = digest;
+
+ string algName = digest.AlgorithmName;
+ if (algName.Equals("NULL"))
+ {
+ this.algId = null;
+ }
+ else
+ {
+ this.algId = new AlgorithmIdentifier(
+ (DerObjectIdentifier)oidMap[digest.AlgorithmName], DerNull.Instance);
+ }
+ }
+
+ public string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "withRSA"; }
+ }
+
+ /**
+ * Initialise the signer for signing or verification.
+ *
+ * @param forSigning true if for signing, false otherwise
+ * @param param necessary parameters.
+ */
+ public void Init(
+ bool forSigning,
+ ICipherParameters parameters)
+ {
+ this.forSigning = forSigning;
+ AsymmetricKeyParameter k;
+
+ if (parameters is ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)parameters;
+ }
+
+ if (forSigning && !k.IsPrivate)
+ throw new InvalidKeyException("Signing requires private key.");
+
+ if (!forSigning && k.IsPrivate)
+ throw new InvalidKeyException("Verification requires public key.");
+
+ Reset();
+
+ rsaEngine.Init(forSigning, parameters);
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void Update(
+ byte input)
+ {
+ digest.Update(input);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ digest.BlockUpdate(input, inOff, length);
+ }
+
+ /**
+ * Generate a signature for the message we've been loaded with using
+ * the key we were initialised with.
+ */
+ public byte[] GenerateSignature()
+ {
+ if (!forSigning)
+ throw new InvalidOperationException("RsaDigestSigner not initialised for signature generation.");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ byte[] data = DerEncode(hash);
+ return rsaEngine.ProcessBlock(data, 0, data.Length);
+ }
+
+ /**
+ * return true if the internal state represents the signature described
+ * in the passed in array.
+ */
+ public bool VerifySignature(
+ byte[] signature)
+ {
+ if (forSigning)
+ throw new InvalidOperationException("RsaDigestSigner not initialised for verification");
+
+ byte[] hash = new byte[digest.GetDigestSize()];
+ digest.DoFinal(hash, 0);
+
+ byte[] sig;
+ byte[] expected;
+
+ try
+ {
+ sig = rsaEngine.ProcessBlock(signature, 0, signature.Length);
+ expected = DerEncode(hash);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ if (sig.Length == expected.Length)
+ {
+ for (int i = 0; i < sig.Length; i++)
+ {
+ if (sig[i] != expected[i])
+ {
+ return false;
+ }
+ }
+ }
+ else if (sig.Length == expected.Length - 2) // NULL left out
+ {
+ int sigOffset = sig.Length - hash.Length - 2;
+ int expectedOffset = expected.Length - hash.Length - 2;
+
+ expected[1] -= 2; // adjust lengths
+ expected[3] -= 2;
+
+ for (int i = 0; i < hash.Length; i++)
+ {
+ if (sig[sigOffset + i] != expected[expectedOffset + i]) // check hash
+ {
+ return false;
+ }
+ }
+
+ for (int i = 0; i < sigOffset; i++)
+ {
+ if (sig[i] != expected[i]) // check header less NULL
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void Reset()
+ {
+ digest.Reset();
+ }
+
+ private byte[] DerEncode(byte[] hash)
+ {
+ if (algId == null)
+ {
+ // For raw RSA, the DigestInfo must be prepared externally
+ return hash;
+ }
+
+ DigestInfo dInfo = new DigestInfo(algId, hash);
+
+ return dInfo.GetDerEncoded();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/AlertDescription.cs b/Crypto/src/crypto/tls/AlertDescription.cs
new file mode 100644
index 000000000..e1229a4a3
--- /dev/null
+++ b/Crypto/src/crypto/tls/AlertDescription.cs
@@ -0,0 +1,47 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 2246 7.2
+ /// </summary>
+ public enum AlertDescription : byte
+ {
+ close_notify = 0,
+ unexpected_message = 10,
+ bad_record_mac = 20,
+ decryption_failed = 21,
+ record_overflow = 22,
+ decompression_failure = 30,
+ handshake_failure = 40,
+ /* 41 is not defined, for historical reasons */
+ bad_certificate = 42,
+ unsupported_certificate = 43,
+ certificate_revoked = 44,
+ certificate_expired = 45,
+ certificate_unknown = 46,
+ illegal_parameter = 47,
+ unknown_ca = 48,
+ access_denied = 49,
+ decode_error = 50,
+ decrypt_error = 51,
+ export_restriction = 60,
+ protocol_version = 70,
+ insufficient_security = 71,
+ internal_error = 80,
+ user_canceled = 90,
+ no_renegotiation = 100,
+
+ /*
+ * RFC 3546
+ */
+ unsupported_extension = 110,
+ certificate_unobtainable = 111,
+ unrecognized_name = 112,
+ bad_certificate_status_response = 113,
+ bad_certificate_hash_value = 114,
+
+ /*
+ * RFC 4279
+ */
+ unknown_psk_identity = 115,
+ }
+}
diff --git a/Crypto/src/crypto/tls/AlertLevel.cs b/Crypto/src/crypto/tls/AlertLevel.cs
new file mode 100644
index 000000000..afb04308b
--- /dev/null
+++ b/Crypto/src/crypto/tls/AlertLevel.cs
@@ -0,0 +1,11 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 2246 7.2
+ /// </summary>
+ public enum AlertLevel : byte
+ {
+ warning = 1,
+ fatal = 2,
+ }
+}
diff --git a/Crypto/src/crypto/tls/AlwaysValidVerifyer.cs b/Crypto/src/crypto/tls/AlwaysValidVerifyer.cs
new file mode 100644
index 000000000..e26c6fc3f
--- /dev/null
+++ b/Crypto/src/crypto/tls/AlwaysValidVerifyer.cs
@@ -0,0 +1,24 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <remarks>
+ /// A certificate verifyer, that will always return true.
+ /// <pre>
+ /// DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING.
+ /// </pre>
+ /// </remarks>
+ [Obsolete("Perform certificate verification in TlsAuthentication implementation")]
+ public class AlwaysValidVerifyer
+ : ICertificateVerifyer
+ {
+ /// <summary>Return true.</summary>
+ public bool IsValid(
+ X509CertificateStructure[] certs)
+ {
+ return true;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/ByteQueue.cs b/Crypto/src/crypto/tls/ByteQueue.cs
new file mode 100644
index 000000000..96062402b
--- /dev/null
+++ b/Crypto/src/crypto/tls/ByteQueue.cs
@@ -0,0 +1,125 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <remarks>
+ /// A queue for bytes.
+ /// <p>
+ /// This file could be more optimized.
+ /// </p>
+ /// </remarks>
+ public class ByteQueue
+ {
+ /// <returns>The smallest number which can be written as 2^x which is bigger than i.</returns>
+ public static int NextTwoPow(
+ int i)
+ {
+ /*
+ * This code is based of a lot of code I found on the Internet
+ * which mostly referenced a book called "Hacking delight".
+ *
+ */
+ i |= (i >> 1);
+ i |= (i >> 2);
+ i |= (i >> 4);
+ i |= (i >> 8);
+ i |= (i >> 16);
+ return i + 1;
+ }
+
+ /**
+ * The initial size for our buffer.
+ */
+ private const int InitBufSize = 1024;
+
+ /**
+ * The buffer where we store our data.
+ */
+ private byte[] databuf = new byte[ByteQueue.InitBufSize];
+
+ /**
+ * How many bytes at the beginning of the buffer are skipped.
+ */
+ private int skipped = 0;
+
+ /**
+ * How many bytes in the buffer are valid data.
+ */
+ private int available = 0;
+
+ /// <summary>Read data from the buffer.</summary>
+ /// <param name="buf">The buffer where the read data will be copied to.</param>
+ /// <param name="offset">How many bytes to skip at the beginning of buf.</param>
+ /// <param name="len">How many bytes to read at all.</param>
+ /// <param name="skip">How many bytes from our data to skip.</param>
+ public void Read(
+ byte[] buf,
+ int offset,
+ int len,
+ int skip)
+ {
+ if ((available - skip) < len)
+ {
+ throw new TlsException("Not enough data to read");
+ }
+ if ((buf.Length - offset) < len)
+ {
+ throw new TlsException("Buffer size of " + buf.Length + " is too small for a read of " + len + " bytes");
+ }
+ Array.Copy(databuf, skipped + skip, buf, offset, len);
+ }
+
+ /// <summary>Add some data to our buffer.</summary>
+ /// <param name="data">A byte-array to read data from.</param>
+ /// <param name="offset">How many bytes to skip at the beginning of the array.</param>
+ /// <param name="len">How many bytes to read from the array.</param>
+ public void AddData(
+ byte[] data,
+ int offset,
+ int len)
+ {
+ if ((skipped + available + len) > databuf.Length)
+ {
+ byte[] tmp = new byte[ByteQueue.NextTwoPow(data.Length)];
+ Array.Copy(databuf, skipped, tmp, 0, available);
+ skipped = 0;
+ databuf = tmp;
+ }
+ Array.Copy(data, offset, databuf, skipped + available, len);
+ available += len;
+ }
+
+ /// <summary>Remove some bytes from our data from the beginning.</summary>
+ /// <param name="i">How many bytes to remove.</param>
+ public void RemoveData(
+ int i)
+ {
+ if (i > available)
+ {
+ throw new TlsException("Cannot remove " + i + " bytes, only got " + available);
+ }
+
+ /*
+ * Skip the data.
+ */
+ available -= i;
+ skipped += i;
+
+ /*
+ * If more than half of our data is skipped, we will move the data
+ * in the buffer.
+ */
+ if (skipped > (databuf.Length / 2))
+ {
+ Array.Copy(databuf, skipped, databuf, 0, available);
+ skipped = 0;
+ }
+ }
+
+ /// <summary>The number of bytes which are available in this buffer.</summary>
+ public int Available
+ {
+ get { return available; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/Certificate.cs b/Crypto/src/crypto/tls/Certificate.cs
new file mode 100644
index 000000000..e4df041e2
--- /dev/null
+++ b/Crypto/src/crypto/tls/Certificate.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /**
+ * A representation for a certificate chain.
+ */
+ public class Certificate
+ {
+ public static readonly Certificate EmptyChain = new Certificate(new X509CertificateStructure[0]);
+
+ /**
+ * The certificates.
+ */
+ internal X509CertificateStructure[] certs;
+
+ /**
+ * Parse the ServerCertificate message.
+ *
+ * @param inStr The stream where to parse from.
+ * @return A Certificate object with the certs, the server has sended.
+ * @throws IOException If something goes wrong during parsing.
+ */
+ internal static Certificate Parse(
+ Stream inStr)
+ {
+ int left = TlsUtilities.ReadUint24(inStr);
+ if (left == 0)
+ {
+ return EmptyChain;
+ }
+ IList tmp = Platform.CreateArrayList();
+ while (left > 0)
+ {
+ int size = TlsUtilities.ReadUint24(inStr);
+ left -= 3 + size;
+ byte[] buf = new byte[size];
+ TlsUtilities.ReadFully(buf, inStr);
+ MemoryStream bis = new MemoryStream(buf, false);
+ Asn1Object o = Asn1Object.FromStream(bis);
+ tmp.Add(X509CertificateStructure.GetInstance(o));
+ if (bis.Position < bis.Length)
+ {
+ throw new ArgumentException("Sorry, there is garbage data left after the certificate");
+ }
+ }
+ X509CertificateStructure[] certs = new X509CertificateStructure[tmp.Count];
+ for (int i = 0; i < tmp.Count; ++i)
+ {
+ certs[i] = (X509CertificateStructure)tmp[i];
+ }
+ return new Certificate(certs);
+ }
+
+ /**
+ * Encodes version of the ClientCertificate message
+ *
+ * @param outStr stream to write the message to
+ * @throws IOException If something goes wrong
+ */
+ internal void Encode(
+ Stream outStr)
+ {
+ IList encCerts = Platform.CreateArrayList();
+ int totalSize = 0;
+ foreach (X509CertificateStructure cert in certs)
+ {
+ byte[] encCert = cert.GetEncoded(Asn1Encodable.Der);
+ encCerts.Add(encCert);
+ totalSize += encCert.Length + 3;
+ }
+
+ TlsUtilities.WriteUint24(totalSize, outStr);
+
+ foreach (byte[] encCert in encCerts)
+ {
+ TlsUtilities.WriteOpaque24(encCert, outStr);
+ }
+ }
+
+ /**
+ * Private constructor from a cert array.
+ *
+ * @param certs The certs the chain should contain.
+ */
+ public Certificate(X509CertificateStructure[] certs)
+ {
+ if (certs == null)
+ throw new ArgumentNullException("certs");
+
+ this.certs = certs;
+ }
+
+ /// <returns>An array which contains the certs, this chain contains.</returns>
+ public X509CertificateStructure[] GetCerts()
+ {
+ return (X509CertificateStructure[]) certs.Clone();
+ }
+
+ public bool IsEmpty
+ {
+ get { return certs.Length == 0; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/CertificateRequest.cs b/Crypto/src/crypto/tls/CertificateRequest.cs
new file mode 100644
index 000000000..49d8ba6fb
--- /dev/null
+++ b/Crypto/src/crypto/tls/CertificateRequest.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class CertificateRequest
+ {
+ private ClientCertificateType[] certificateTypes;
+ private IList certificateAuthorities;
+
+ public CertificateRequest(ClientCertificateType[] certificateTypes, IList certificateAuthorities)
+ {
+ this.certificateTypes = certificateTypes;
+ this.certificateAuthorities = certificateAuthorities;
+ }
+
+ public ClientCertificateType[] CertificateTypes
+ {
+ get { return certificateTypes; }
+ }
+
+ /// <returns>A <see cref="IList"/> of X509Name</returns>
+ public IList CertificateAuthorities
+ {
+ get { return certificateAuthorities; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/tls/CipherSuite.cs b/Crypto/src/crypto/tls/CipherSuite.cs
new file mode 100644
index 000000000..6e1f7a545
--- /dev/null
+++ b/Crypto/src/crypto/tls/CipherSuite.cs
@@ -0,0 +1,136 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 2246 A.5
+ /// </summary>
+ public enum CipherSuite : int
+ {
+ TLS_NULL_WITH_NULL_NULL = 0x0000,
+ TLS_RSA_WITH_NULL_MD5 = 0x0001,
+ TLS_RSA_WITH_NULL_SHA = 0x0002,
+ TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003,
+ TLS_RSA_WITH_RC4_128_MD5 = 0x0004,
+ TLS_RSA_WITH_RC4_128_SHA = 0x0005,
+ TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006,
+ TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007,
+ TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008,
+ TLS_RSA_WITH_DES_CBC_SHA = 0x0009,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A,
+ TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B,
+ TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C,
+ TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D,
+ TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E,
+ TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F,
+ TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010,
+ TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011,
+ TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012,
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013,
+ TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014,
+ TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015,
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016,
+ TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017,
+ TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018,
+ TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019,
+ TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A,
+ TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B,
+
+ /*
+ * RFC 3268
+ */
+ TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F,
+ TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030,
+ TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031,
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
+ TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034,
+ TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035,
+ TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036,
+ TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037,
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
+ TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A,
+
+ /*
+ * RFC 4279
+ */
+ TLS_PSK_WITH_RC4_128_SHA = 0x008A,
+ TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B,
+ TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C,
+ TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D,
+ TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E,
+ TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F,
+ TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090,
+ TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091,
+ TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092,
+ TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093,
+ TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094,
+ TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095,
+
+ /*
+ * RFC 4492
+ */
+ TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001,
+ TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002,
+ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003,
+ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004,
+ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005,
+ TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006,
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007,
+ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
+ TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B,
+ TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C,
+ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D,
+ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E,
+ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F,
+ TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010,
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011,
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012,
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
+ TLS_ECDH_anon_WITH_NULL_SHA = 0xC015,
+ TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016,
+ TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017,
+ TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018,
+ TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019,
+
+ /*
+ * RFC 5054
+ */
+ TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A,
+ TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B,
+ TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C,
+ TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D,
+ TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E,
+ TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F,
+ TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020,
+ TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021,
+ TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022,
+
+ /*
+ * RFC 5289
+ */
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
+ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025,
+ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026,
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
+ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029,
+ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A,
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
+ TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D,
+ TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
+ TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031,
+ TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032,
+
+ /*
+ * RFC 5746
+ */
+ TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
+ }
+}
diff --git a/Crypto/src/crypto/tls/ClientCertificateType.cs b/Crypto/src/crypto/tls/ClientCertificateType.cs
new file mode 100644
index 000000000..58f5d4276
--- /dev/null
+++ b/Crypto/src/crypto/tls/ClientCertificateType.cs
@@ -0,0 +1,20 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 2246 7.4.4
+ /// </summary>
+ public enum ClientCertificateType : byte
+ {
+ rsa_sign = 1,
+ dss_sign = 2,
+ rsa_fixed_dh = 3,
+ dss_fixed_dh = 4,
+
+ /*
+ * RFC 4492 5.5
+ */
+ ecdsa_sign = 64,
+ rsa_fixed_ecdh = 65,
+ ecdsa_fixed_ecdh = 66,
+ }
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/tls/CombinedHash.cs b/Crypto/src/crypto/tls/CombinedHash.cs
new file mode 100644
index 000000000..59ad87a7b
--- /dev/null
+++ b/Crypto/src/crypto/tls/CombinedHash.cs
@@ -0,0 +1,82 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <remarks>A combined hash, which implements md5(m) || sha1(m).</remarks>
+ internal class CombinedHash
+ : IDigest
+ {
+ private readonly MD5Digest md5;
+ private readonly Sha1Digest sha1;
+
+ internal CombinedHash()
+ {
+ this.md5 = new MD5Digest();
+ this.sha1 = new Sha1Digest();
+ }
+
+ internal CombinedHash(CombinedHash t)
+ {
+ this.md5 = new MD5Digest(t.md5);
+ this.sha1 = new Sha1Digest(t.sha1);
+ }
+
+ /// <seealso cref="IDigest.AlgorithmName"/>
+ public string AlgorithmName
+ {
+ get
+ {
+ return md5.AlgorithmName + " and " + sha1.AlgorithmName + " for TLS 1.0";
+ }
+ }
+
+ /// <seealso cref="IDigest.GetByteLength"/>
+ public int GetByteLength()
+ {
+ return System.Math.Max(md5.GetByteLength(), sha1.GetByteLength());
+ }
+
+ /// <seealso cref="IDigest.GetDigestSize"/>
+ public int GetDigestSize()
+ {
+ return md5.GetDigestSize() + sha1.GetDigestSize();
+ }
+
+ /// <seealso cref="IDigest.Update"/>
+ public void Update(
+ byte input)
+ {
+ md5.Update(input);
+ sha1.Update(input);
+ }
+
+ /// <seealso cref="IDigest.BlockUpdate"/>
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int len)
+ {
+ md5.BlockUpdate(input, inOff, len);
+ sha1.BlockUpdate(input, inOff, len);
+ }
+
+ /// <seealso cref="IDigest.DoFinal"/>
+ public int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ int i1 = md5.DoFinal(output, outOff);
+ int i2 = sha1.DoFinal(output, outOff + i1);
+ return i1 + i2;
+ }
+
+ /// <seealso cref="IDigest.Reset"/>
+ public void Reset()
+ {
+ md5.Reset();
+ sha1.Reset();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/CompressionMethod.cs b/Crypto/src/crypto/tls/CompressionMethod.cs
new file mode 100644
index 000000000..4a127a63e
--- /dev/null
+++ b/Crypto/src/crypto/tls/CompressionMethod.cs
@@ -0,0 +1,20 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 2246 6.1
+ /// </summary>
+ public enum CompressionMethod : byte
+ {
+ NULL = 0,
+
+ /*
+ * RFC 3749 2
+ */
+ DEFLATE = 1
+
+ /*
+ * Values from 224 decimal (0xE0) through 255 decimal (0xFF)
+ * inclusive are reserved for private use.
+ */
+ }
+}
diff --git a/Crypto/src/crypto/tls/ContentType.cs b/Crypto/src/crypto/tls/ContentType.cs
new file mode 100644
index 000000000..a664e3a38
--- /dev/null
+++ b/Crypto/src/crypto/tls/ContentType.cs
@@ -0,0 +1,13 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 2246 6.2.1
+ /// </summary>
+ public enum ContentType : byte
+ {
+ change_cipher_spec = 20,
+ alert = 21,
+ handshake = 22,
+ application_data = 23,
+ }
+}
diff --git a/Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs b/Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
new file mode 100644
index 000000000..2dfe526d1
--- /dev/null
+++ b/Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
@@ -0,0 +1,67 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class DefaultTlsAgreementCredentials
+ : TlsAgreementCredentials
+ {
+ protected Certificate clientCert;
+ protected AsymmetricKeyParameter clientPrivateKey;
+
+ protected IBasicAgreement basicAgreement;
+
+ public DefaultTlsAgreementCredentials(Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey)
+ {
+ if (clientCertificate == null)
+ {
+ throw new ArgumentNullException("clientCertificate");
+ }
+ if (clientCertificate.certs.Length == 0)
+ {
+ throw new ArgumentException("cannot be empty", "clientCertificate");
+ }
+ if (clientPrivateKey == null)
+ {
+ throw new ArgumentNullException("clientPrivateKey");
+ }
+ if (!clientPrivateKey.IsPrivate)
+ {
+ throw new ArgumentException("must be private", "clientPrivateKey");
+ }
+
+ if (clientPrivateKey is DHPrivateKeyParameters)
+ {
+ basicAgreement = new DHBasicAgreement();
+ }
+ else if (clientPrivateKey is ECPrivateKeyParameters)
+ {
+ basicAgreement = new ECDHBasicAgreement();
+ }
+ else
+ {
+ throw new ArgumentException("type not supported: "
+ + clientPrivateKey.GetType().FullName, "clientPrivateKey");
+ }
+
+ this.clientCert = clientCertificate;
+ this.clientPrivateKey = clientPrivateKey;
+ }
+
+ public virtual Certificate Certificate
+ {
+ get { return clientCert; }
+ }
+
+ public virtual byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey)
+ {
+ basicAgreement.Init(clientPrivateKey);
+ BigInteger agreementValue = basicAgreement.CalculateAgreement(serverPublicKey);
+ return BigIntegers.AsUnsignedByteArray(agreementValue);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs b/Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
new file mode 100644
index 000000000..53e3438d9
--- /dev/null
+++ b/Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
@@ -0,0 +1,73 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Modes;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class DefaultTlsCipherFactory
+ : TlsCipherFactory
+ {
+ public virtual TlsCipher CreateCipher(TlsClientContext context,
+ EncryptionAlgorithm encryptionAlgorithm, DigestAlgorithm digestAlgorithm)
+ {
+ switch (encryptionAlgorithm)
+ {
+ case EncryptionAlgorithm.cls_3DES_EDE_CBC:
+ return CreateDesEdeCipher(context, 24, digestAlgorithm);
+ case EncryptionAlgorithm.AES_128_CBC:
+ return CreateAesCipher(context, 16, digestAlgorithm);
+ case EncryptionAlgorithm.AES_256_CBC:
+ return CreateAesCipher(context, 32, digestAlgorithm);
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsCipher CreateAesCipher(TlsClientContext context, int cipherKeySize,
+ DigestAlgorithm digestAlgorithm)
+ {
+ return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(),
+ CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsCipher CreateDesEdeCipher(TlsClientContext context, int cipherKeySize,
+ DigestAlgorithm digestAlgorithm)
+ {
+ return new TlsBlockCipher(context, CreateDesEdeBlockCipher(), CreateDesEdeBlockCipher(),
+ CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+ }
+
+ protected virtual IBlockCipher CreateAesBlockCipher()
+ {
+ return new CbcBlockCipher(new AesFastEngine());
+ }
+
+ protected virtual IBlockCipher CreateDesEdeBlockCipher()
+ {
+ return new CbcBlockCipher(new DesEdeEngine());
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual IDigest CreateDigest(DigestAlgorithm digestAlgorithm)
+ {
+ switch (digestAlgorithm)
+ {
+ case DigestAlgorithm.MD5:
+ return new MD5Digest();
+ case DigestAlgorithm.SHA:
+ return new Sha1Digest();
+ case DigestAlgorithm.SHA256:
+ return new Sha256Digest();
+ case DigestAlgorithm.SHA384:
+ return new Sha384Digest();
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/DefaultTlsClient.cs b/Crypto/src/crypto/tls/DefaultTlsClient.cs
new file mode 100644
index 000000000..c5b59a06b
--- /dev/null
+++ b/Crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -0,0 +1,259 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class DefaultTlsClient
+ : TlsClient
+ {
+ protected TlsCipherFactory cipherFactory;
+
+ protected TlsClientContext context;
+
+ protected CompressionMethod selectedCompressionMethod;
+ protected CipherSuite selectedCipherSuite;
+
+ public DefaultTlsClient()
+ : this(new DefaultTlsCipherFactory())
+ {
+ }
+
+ public DefaultTlsClient(TlsCipherFactory cipherFactory)
+ {
+ this.cipherFactory = cipherFactory;
+ }
+
+ public virtual void Init(TlsClientContext context)
+ {
+ this.context = context;
+ }
+
+ public virtual CipherSuite[] GetCipherSuites()
+ {
+ return new CipherSuite[] {
+ CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ };
+ }
+
+ public virtual CompressionMethod[] GetCompressionMethods()
+ {
+ /*
+ * To offer DEFLATE compression, override this method:
+ * return new CompressionMethod[] { CompressionMethod.DEFLATE, CompressionMethod.NULL };
+ */
+
+ return new CompressionMethod[] { CompressionMethod.NULL };
+ }
+
+ public virtual IDictionary GetClientExtensions()
+ {
+ return null;
+ }
+
+ public virtual void NotifySessionID(byte[] sessionID)
+ {
+ // Currently ignored
+ }
+
+ public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite)
+ {
+ this.selectedCipherSuite = selectedCipherSuite;
+ }
+
+ public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod)
+ {
+ this.selectedCompressionMethod = selectedCompressionMethod;
+ }
+
+ public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+ {
+ if (!secureRenegotiation)
+ {
+ /*
+ * RFC 5746 3.4.
+ * If the extension is not present, the server does not support
+ * secure renegotiation; set secure_renegotiation flag to FALSE.
+ * In this case, some clients may want to terminate the handshake
+ * instead of continuing; see Section 4.1 for discussion.
+ */
+// throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+ {
+ }
+
+ public virtual TlsKeyExchange GetKeyExchange()
+ {
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ return CreateRsaKeyExchange();
+
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
+
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
+
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocolHandler verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher
+ * suites, so if we now can't produce an implementation, we shouldn't have
+ * offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public abstract TlsAuthentication GetAuthentication();
+
+ public virtual TlsCompression GetCompression()
+ {
+ switch (selectedCompressionMethod)
+ {
+ case CompressionMethod.NULL:
+ return new TlsNullCompression();
+
+ case CompressionMethod.DEFLATE:
+ return new TlsDeflateCompression();
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocolHandler verifies that the
+ * server-selected compression method was in the list of client-offered compression
+ * methods, so if we now can't produce an implementation, we shouldn't have
+ * offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual TlsCipher GetCipher()
+ {
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA);
+
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA);
+
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocolHandler verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher
+ * suites, so if we now can't produce an implementation, we shouldn't have
+ * offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected virtual TlsKeyExchange CreateDHKeyExchange(KeyExchangeAlgorithm keyExchange)
+ {
+ return new TlsDHKeyExchange(context, keyExchange);
+ }
+
+ protected virtual TlsKeyExchange CreateDheKeyExchange(KeyExchangeAlgorithm keyExchange)
+ {
+ return new TlsDheKeyExchange(context, keyExchange);
+ }
+
+ protected virtual TlsKeyExchange CreateECDHKeyExchange(KeyExchangeAlgorithm keyExchange)
+ {
+ return new TlsECDHKeyExchange(context, keyExchange);
+ }
+
+ protected virtual TlsKeyExchange CreateECDheKeyExchange(KeyExchangeAlgorithm keyExchange)
+ {
+ return new TlsECDheKeyExchange(context, keyExchange);
+ }
+
+ protected virtual TlsKeyExchange CreateRsaKeyExchange()
+ {
+ return new TlsRsaKeyExchange(context);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs b/Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
new file mode 100644
index 000000000..23d607d85
--- /dev/null
+++ b/Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
@@ -0,0 +1,76 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class DefaultTlsSignerCredentials
+ : TlsSignerCredentials
+ {
+ protected TlsClientContext context;
+ protected Certificate clientCert;
+ protected AsymmetricKeyParameter clientPrivateKey;
+
+ protected TlsSigner clientSigner;
+
+ public DefaultTlsSignerCredentials(TlsClientContext context,
+ Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey)
+ {
+ if (clientCertificate == null)
+ {
+ throw new ArgumentNullException("clientCertificate");
+ }
+ if (clientCertificate.certs.Length == 0)
+ {
+ throw new ArgumentException("cannot be empty", "clientCertificate");
+ }
+ if (clientPrivateKey == null)
+ {
+ throw new ArgumentNullException("clientPrivateKey");
+ }
+ if (!clientPrivateKey.IsPrivate)
+ {
+ throw new ArgumentException("must be private", "clientPrivateKey");
+ }
+
+ if (clientPrivateKey is RsaKeyParameters)
+ {
+ clientSigner = new TlsRsaSigner();
+ }
+ else if (clientPrivateKey is DsaPrivateKeyParameters)
+ {
+ clientSigner = new TlsDssSigner();
+ }
+ else if (clientPrivateKey is ECPrivateKeyParameters)
+ {
+ clientSigner = new TlsECDsaSigner();
+ }
+ else
+ {
+ throw new ArgumentException("type not supported: "
+ + clientPrivateKey.GetType().FullName, "clientPrivateKey");
+ }
+
+ this.context = context;
+ this.clientCert = clientCertificate;
+ this.clientPrivateKey = clientPrivateKey;
+ }
+
+ public virtual Certificate Certificate
+ {
+ get { return clientCert; }
+ }
+
+ public virtual byte[] GenerateCertificateSignature(byte[] md5andsha1)
+ {
+ try
+ {
+ return clientSigner.CalculateRawSignature(context.SecureRandom, clientPrivateKey, md5andsha1);
+ }
+ catch (CryptoException)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/DigestAlgorithm.cs b/Crypto/src/crypto/tls/DigestAlgorithm.cs
new file mode 100644
index 000000000..cede6b7f8
--- /dev/null
+++ b/Crypto/src/crypto/tls/DigestAlgorithm.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public enum DigestAlgorithm
+ {
+ /*
+ * Note that the values here are implementation-specific and arbitrary.
+ * It is recommended not to depend on the particular values (e.g. serialization).
+ */
+ NULL,
+ MD5,
+ SHA,
+
+ /*
+ * RFC 5289
+ */
+ SHA256,
+ SHA384,
+ }
+}
diff --git a/Crypto/src/crypto/tls/ECCurveType.cs b/Crypto/src/crypto/tls/ECCurveType.cs
new file mode 100644
index 000000000..15d5d7b42
--- /dev/null
+++ b/Crypto/src/crypto/tls/ECCurveType.cs
@@ -0,0 +1,29 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 4492 5.4
+ /// </summary>
+ public enum ECCurveType : byte
+ {
+ /**
+ * Indicates the elliptic curve domain parameters are conveyed verbosely, and the
+ * underlying finite field is a prime field.
+ */
+ explicit_prime = 1,
+
+ /**
+ * Indicates the elliptic curve domain parameters are conveyed verbosely, and the
+ * underlying finite field is a characteristic-2 field.
+ */
+ explicit_char2 = 2,
+
+ /**
+ * Indicates that a named curve is used. This option SHOULD be used when applicable.
+ */
+ named_curve = 3,
+
+ /*
+ * Values 248 through 255 are reserved for private use.
+ */
+ }
+}
diff --git a/Crypto/src/crypto/tls/ECPointFormat.cs b/Crypto/src/crypto/tls/ECPointFormat.cs
new file mode 100644
index 000000000..4e0dd0067
--- /dev/null
+++ b/Crypto/src/crypto/tls/ECPointFormat.cs
@@ -0,0 +1,16 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 4492 5.1.2
+ /// </summary>
+ public enum ECPointFormat : byte
+ {
+ uncompressed = 0,
+ ansiX962_compressed_prime = 1,
+ ansiX962_compressed_char2 = 2,
+
+ /*
+ * reserved (248..255)
+ */
+ }
+}
diff --git a/Crypto/src/crypto/tls/EncryptionAlgorithm.cs b/Crypto/src/crypto/tls/EncryptionAlgorithm.cs
new file mode 100644
index 000000000..79d3b63b5
--- /dev/null
+++ b/Crypto/src/crypto/tls/EncryptionAlgorithm.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public enum EncryptionAlgorithm
+ {
+ /*
+ * Note that the values here are implementation-specific and arbitrary.
+ * It is recommended not to depend on the particular values (e.g. serialization).
+ */
+ NULL,
+ RC4_40,
+ RC4_128,
+ RC2_CBC_40,
+ IDEA_CBC,
+ DES40_CBC,
+ DES_CBC,
+ cls_3DES_EDE_CBC,
+
+ /*
+ * RFC 3268
+ */
+ AES_128_CBC,
+ AES_256_CBC,
+
+ /*
+ * RFC 5289
+ */
+ AES_128_GCM,
+ AES_256_GCM,
+ }
+}
diff --git a/Crypto/src/crypto/tls/ExtensionType.cs b/Crypto/src/crypto/tls/ExtensionType.cs
new file mode 100644
index 000000000..f00e34e3f
--- /dev/null
+++ b/Crypto/src/crypto/tls/ExtensionType.cs
@@ -0,0 +1,31 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 4366 2.3
+ /// </summary>
+ public enum ExtensionType : int
+ {
+ server_name = 0,
+ max_fragment_length = 1,
+ client_certificate_url = 2,
+ trusted_ca_keys = 3,
+ truncated_hmac = 4,
+ status_request = 5,
+
+ /*
+ * RFC 4492
+ */
+ elliptic_curves = 10,
+ ec_point_formats = 11,
+
+ /*
+ * RFC 5054 2.8.1
+ */
+ srp = 12,
+
+ /*
+ * RFC 5746 6
+ */
+ renegotiation_info = 0xff01,
+ }
+}
diff --git a/Crypto/src/crypto/tls/HandshakeType.cs b/Crypto/src/crypto/tls/HandshakeType.cs
new file mode 100644
index 000000000..deedb1f84
--- /dev/null
+++ b/Crypto/src/crypto/tls/HandshakeType.cs
@@ -0,0 +1,19 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 2246 7.4
+ /// </summary>
+ public enum HandshakeType : byte
+ {
+ hello_request = 0,
+ client_hello = 1,
+ server_hello = 2,
+ certificate = 11,
+ server_key_exchange = 12,
+ certificate_request = 13,
+ server_hello_done = 14,
+ certificate_verify = 15,
+ client_key_exchange = 16,
+ finished = 20,
+ }
+}
diff --git a/Crypto/src/crypto/tls/ICertificateVerifyer.cs b/Crypto/src/crypto/tls/ICertificateVerifyer.cs
new file mode 100644
index 000000000..df5ea51d7
--- /dev/null
+++ b/Crypto/src/crypto/tls/ICertificateVerifyer.cs
@@ -0,0 +1,18 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <remarks>
+ /// This should be implemented by any class which can find out, if a given
+ /// certificate chain is being accepted by an client.
+ /// </remarks>
+ [Obsolete("Perform certificate verification in TlsAuthentication implementation")]
+ public interface ICertificateVerifyer
+ {
+ /// <param name="certs">The certs, which are part of the chain.</param>
+ /// <returns>True, if the chain is accepted, false otherwise</returns>
+ bool IsValid(X509CertificateStructure[] certs);
+ }
+}
diff --git a/Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs b/Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs
new file mode 100644
index 000000000..3fdbeb2a6
--- /dev/null
+++ b/Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public enum KeyExchangeAlgorithm
+ {
+ /*
+ * Note that the values here are implementation-specific and arbitrary.
+ * It is recommended not to depend on the particular values (e.g. serialization).
+ */
+ NULL,
+ RSA,
+ RSA_EXPORT,
+ DHE_DSS,
+ DHE_DSS_EXPORT,
+ DHE_RSA,
+ DHE_RSA_EXPORT,
+ DH_DSS,
+ DH_DSS_EXPORT,
+ DH_RSA,
+ DH_RSA_EXPORT,
+ DH_anon,
+ DH_anon_export,
+ PSK,
+ DHE_PSK,
+ RSA_PSK,
+ ECDH_ECDSA,
+ ECDHE_ECDSA,
+ ECDH_RSA,
+ ECDHE_RSA,
+ ECDH_anon,
+ SRP,
+ SRP_DSS,
+ SRP_RSA,
+ }
+}
diff --git a/Crypto/src/crypto/tls/LegacyTlsAuthentication.cs b/Crypto/src/crypto/tls/LegacyTlsAuthentication.cs
new file mode 100644
index 000000000..395f94208
--- /dev/null
+++ b/Crypto/src/crypto/tls/LegacyTlsAuthentication.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// A temporary class to wrap old CertificateVerifyer stuff for new TlsAuthentication.
+ /// </summary>
+ [Obsolete]
+ public class LegacyTlsAuthentication
+ : TlsAuthentication
+ {
+ protected ICertificateVerifyer verifyer;
+
+ public LegacyTlsAuthentication(ICertificateVerifyer verifyer)
+ {
+ this.verifyer = verifyer;
+ }
+
+ public virtual void NotifyServerCertificate(Certificate serverCertificate)
+ {
+ if (!this.verifyer.IsValid(serverCertificate.GetCerts()))
+ throw new TlsFatalAlert(AlertDescription.user_canceled);
+ }
+
+ public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ return null;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/LegacyTlsClient.cs b/Crypto/src/crypto/tls/LegacyTlsClient.cs
new file mode 100644
index 000000000..fbb9a732e
--- /dev/null
+++ b/Crypto/src/crypto/tls/LegacyTlsClient.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// A temporary class to use LegacyTlsAuthentication
+ /// </summary>
+ [Obsolete]
+ public class LegacyTlsClient
+ : DefaultTlsClient
+ {
+ [Obsolete]
+ protected ICertificateVerifyer verifyer;
+
+ [Obsolete]
+ public LegacyTlsClient(ICertificateVerifyer verifyer)
+ {
+ this.verifyer = verifyer;
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new LegacyTlsAuthentication(verifyer);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/tls/NamedCurve.cs b/Crypto/src/crypto/tls/NamedCurve.cs
new file mode 100644
index 000000000..c8ee189aa
--- /dev/null
+++ b/Crypto/src/crypto/tls/NamedCurve.cs
@@ -0,0 +1,72 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// RFC 4492 5.1.1
+ /// The named curves defined here are those specified in SEC 2 [13]. Note that many of
+ /// these curves are also recommended in ANSI X9.62 [7] and FIPS 186-2 [11]. Values 0xFE00
+ /// through 0xFEFF are reserved for private use. Values 0xFF01 and 0xFF02 indicate that the
+ /// client supports arbitrary prime and characteristic-2 curves, respectively (the curve
+ /// parameters must be encoded explicitly in ECParameters).
+ /// </summary>
+ public enum NamedCurve : int
+ {
+ sect163k1 = 1,
+ sect163r1 = 2,
+ sect163r2 = 3,
+ sect193r1 = 4,
+ sect193r2 = 5,
+ sect233k1 = 6,
+ sect233r1 = 7,
+ sect239k1 = 8,
+ sect283k1 = 9,
+ sect283r1 = 10,
+ sect409k1 = 11,
+ sect409r1 = 12,
+ sect571k1 = 13,
+ sect571r1 = 14,
+ secp160k1 = 15,
+ secp160r1 = 16,
+ secp160r2 = 17,
+ secp192k1 = 18,
+ secp192r1 = 19,
+ secp224k1 = 20,
+ secp224r1 = 21,
+ secp256k1 = 22,
+ secp256r1 = 23,
+ secp384r1 = 24,
+ secp521r1 = 25,
+
+ /*
+ * reserved (0xFE00..0xFEFF)
+ */
+
+ arbitrary_explicit_prime_curves = 0xFF01,
+ arbitrary_explicit_char2_curves = 0xFF02,
+ }
+
+ internal class NamedCurveHelper
+ {
+ internal static ECDomainParameters GetECParameters(NamedCurve namedCurve)
+ {
+ if (!Enum.IsDefined(typeof(NamedCurve), namedCurve))
+ return null;
+
+ string curveName = namedCurve.ToString();
+
+ // Lazily created the first time a particular curve is accessed
+ X9ECParameters ecP = SecNamedCurves.GetByName(curveName);
+
+ if (ecP == null)
+ return null;
+
+ // It's a bit inefficient to do this conversion every time
+ return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/PskTlsClient.cs b/Crypto/src/crypto/tls/PskTlsClient.cs
new file mode 100644
index 000000000..16975e713
--- /dev/null
+++ b/Crypto/src/crypto/tls/PskTlsClient.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class PskTlsClient
+ :TlsClient
+ {
+ protected TlsCipherFactory cipherFactory;
+ protected TlsPskIdentity pskIdentity;
+
+ protected TlsClientContext context;
+
+ protected CompressionMethod selectedCompressionMethod;
+ protected CipherSuite selectedCipherSuite;
+
+ public PskTlsClient(TlsPskIdentity pskIdentity)
+ : this(new DefaultTlsCipherFactory(), pskIdentity)
+ {
+ }
+
+ public PskTlsClient(TlsCipherFactory cipherFactory, TlsPskIdentity pskIdentity)
+ {
+ this.cipherFactory = cipherFactory;
+ this.pskIdentity = pskIdentity;
+ }
+
+ public virtual void Init(TlsClientContext context)
+ {
+ this.context = context;
+ }
+
+ public virtual CipherSuite[] GetCipherSuites()
+ {
+ return new CipherSuite[] {
+ CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
+ CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
+ CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA,
+ };
+ }
+
+ public virtual IDictionary GetClientExtensions()
+ {
+ return null;
+ }
+
+ public virtual CompressionMethod[] GetCompressionMethods()
+ {
+ return new CompressionMethod[] { CompressionMethod.NULL };
+ }
+
+ public virtual void NotifySessionID(byte[] sessionID)
+ {
+ // Currently ignored
+ }
+
+ public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite)
+ {
+ this.selectedCipherSuite = selectedCipherSuite;
+ }
+
+ public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod)
+ {
+ this.selectedCompressionMethod = selectedCompressionMethod;
+ }
+
+ public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+ {
+ if (!secureRenegotiation)
+ {
+ /*
+ * RFC 5746 3.4. If the extension is not present, the server does not support
+ * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
+ * some clients may want to terminate the handshake instead of continuing; see
+ * Section 4.1 for discussion.
+ */
+// throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+ {
+ }
+
+ public virtual TlsKeyExchange GetKeyExchange()
+ {
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
+
+ case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocolHandler verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher
+ * suites, so if we now can't produce an implementation, we shouldn't have
+ * offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual TlsAuthentication GetAuthentication()
+ {
+ return null;
+ }
+
+ public virtual TlsCompression GetCompression()
+ {
+ switch (selectedCompressionMethod)
+ {
+ case CompressionMethod.NULL:
+ return new TlsNullCompression();
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocolHandler verifies that the
+ * server-selected compression method was in the list of client-offered compression
+ * methods, so if we now can't produce an implementation, we shouldn't have
+ * offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual TlsCipher GetCipher()
+ {
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
+ DigestAlgorithm.SHA);
+
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
+ DigestAlgorithm.SHA);
+
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
+ DigestAlgorithm.SHA);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocolHandler verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher
+ * suites, so if we now can't produce an implementation, we shouldn't have
+ * offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected virtual TlsKeyExchange CreatePskKeyExchange(KeyExchangeAlgorithm keyExchange)
+ {
+ return new TlsPskKeyExchange(context, keyExchange, pskIdentity);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/RecordStream.cs b/Crypto/src/crypto/tls/RecordStream.cs
new file mode 100644
index 000000000..e18894b4e
--- /dev/null
+++ b/Crypto/src/crypto/tls/RecordStream.cs
@@ -0,0 +1,166 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <remarks>An implementation of the TLS 1.0 record layer.</remarks>
+ internal class RecordStream
+ {
+ private TlsProtocolHandler handler;
+ private Stream inStr;
+ private Stream outStr;
+ private CombinedHash hash;
+ private TlsCompression readCompression = null;
+ private TlsCompression writeCompression = null;
+ private TlsCipher readCipher = null;
+ private TlsCipher writeCipher = null;
+ private MemoryStream buffer = new MemoryStream();
+
+ internal RecordStream(
+ TlsProtocolHandler handler,
+ Stream inStr,
+ Stream outStr)
+ {
+ this.handler = handler;
+ this.inStr = inStr;
+ this.outStr = outStr;
+ this.hash = new CombinedHash();
+ this.readCompression = new TlsNullCompression();
+ this.writeCompression = this.readCompression;
+ this.readCipher = new TlsNullCipher();
+ this.writeCipher = this.readCipher;
+ }
+
+ internal void ClientCipherSpecDecided(TlsCompression tlsCompression, TlsCipher tlsCipher)
+ {
+ this.writeCompression = tlsCompression;
+ this.writeCipher = tlsCipher;
+ }
+
+ internal void ServerClientSpecReceived()
+ {
+ this.readCompression = this.writeCompression;
+ this.readCipher = this.writeCipher;
+ }
+
+ public void ReadData()
+ {
+ ContentType type = (ContentType)TlsUtilities.ReadUint8(inStr);
+ TlsUtilities.CheckVersion(inStr);
+ int size = TlsUtilities.ReadUint16(inStr);
+ byte[] buf = DecodeAndVerify(type, inStr, size);
+ handler.ProcessData(type, buf, 0, buf.Length);
+ }
+
+ internal byte[] DecodeAndVerify(
+ ContentType type,
+ Stream inStr,
+ int len)
+ {
+ byte[] buf = new byte[len];
+ TlsUtilities.ReadFully(buf, inStr);
+ byte[] decoded = readCipher.DecodeCiphertext(type, buf, 0, buf.Length);
+
+ Stream cOut = readCompression.Decompress(buffer);
+
+ if (cOut == buffer)
+ {
+ return decoded;
+ }
+
+ cOut.Write(decoded, 0, decoded.Length);
+ cOut.Flush();
+ byte[] contents = buffer.ToArray();
+ buffer.SetLength(0);
+ return contents;
+ }
+
+ internal void WriteMessage(
+ ContentType type,
+ byte[] message,
+ int offset,
+ int len)
+ {
+ if (type == ContentType.handshake)
+ {
+ UpdateHandshakeData(message, offset, len);
+ }
+
+ Stream cOut = writeCompression.Compress(buffer);
+
+ byte[] ciphertext;
+ if (cOut == buffer)
+ {
+ ciphertext = writeCipher.EncodePlaintext(type, message, offset, len);
+ }
+ else
+ {
+ cOut.Write(message, offset, len);
+ cOut.Flush();
+ ciphertext = writeCipher.EncodePlaintext(type, buffer.ToArray(), 0, (int)buffer.Position);
+ buffer.SetLength(0);
+ }
+
+ byte[] writeMessage = new byte[ciphertext.Length + 5];
+ TlsUtilities.WriteUint8((byte)type, writeMessage, 0);
+ TlsUtilities.WriteVersion(writeMessage, 1);
+ TlsUtilities.WriteUint16(ciphertext.Length, writeMessage, 3);
+ Array.Copy(ciphertext, 0, writeMessage, 5, ciphertext.Length);
+ outStr.Write(writeMessage, 0, writeMessage.Length);
+ outStr.Flush();
+ }
+
+ internal void UpdateHandshakeData(
+ byte[] message,
+ int offset,
+ int len)
+ {
+ hash.BlockUpdate(message, offset, len);
+ }
+
+ internal byte[] GetCurrentHash()
+ {
+ return DoFinal(new CombinedHash(hash));
+ }
+
+ internal void Close()
+ {
+ IOException e = null;
+ try
+ {
+ inStr.Dispose();
+ }
+ catch (IOException ex)
+ {
+ e = ex;
+ }
+
+ try
+ {
+ // NB: This is harmless if outStr == inStr
+ outStr.Dispose();
+ }
+ catch (IOException ex)
+ {
+ e = ex;
+ }
+
+ if (e != null)
+ {
+ throw e;
+ }
+ }
+
+ internal void Flush()
+ {
+ outStr.Flush();
+ }
+
+ private static byte[] DoFinal(CombinedHash ch)
+ {
+ byte[] bs = new byte[ch.GetDigestSize()];
+ ch.DoFinal(bs, 0);
+ return bs;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/SecurityParameters.cs b/Crypto/src/crypto/tls/SecurityParameters.cs
new file mode 100644
index 000000000..9ed3969eb
--- /dev/null
+++ b/Crypto/src/crypto/tls/SecurityParameters.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class SecurityParameters
+ {
+ internal byte[] clientRandom = null;
+ internal byte[] serverRandom = null;
+ internal byte[] masterSecret = null;
+
+ public byte[] ClientRandom
+ {
+ get { return clientRandom; }
+ }
+
+ public byte[] ServerRandom
+ {
+ get { return serverRandom; }
+ }
+
+ public byte[] MasterSecret
+ {
+ get { return masterSecret; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/SrpTlsClient.cs b/Crypto/src/crypto/tls/SrpTlsClient.cs
new file mode 100644
index 000000000..6c2638bb3
--- /dev/null
+++ b/Crypto/src/crypto/tls/SrpTlsClient.cs
@@ -0,0 +1,188 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class SrpTlsClient
+ : TlsClient
+ {
+ protected TlsCipherFactory cipherFactory;
+ protected byte[] identity;
+ protected byte[] password;
+
+ protected TlsClientContext context;
+
+ protected CompressionMethod selectedCompressionMethod;
+ protected CipherSuite selectedCipherSuite;
+
+ public SrpTlsClient(byte[] identity, byte[] password)
+ : this(new DefaultTlsCipherFactory(), identity, password)
+ {
+ }
+
+ public SrpTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password)
+ {
+ this.cipherFactory = cipherFactory;
+ this.identity = Arrays.Clone(identity);
+ this.password = Arrays.Clone(password);
+ }
+
+ public virtual void Init(TlsClientContext context)
+ {
+ this.context = context;
+ }
+
+ public virtual CipherSuite[] GetCipherSuites()
+ {
+ return new CipherSuite[] {
+ CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
+ };
+ }
+
+ public virtual IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = Platform.CreateHashtable();
+
+ MemoryStream srpData = new MemoryStream();
+ TlsUtilities.WriteOpaque8(this.identity, srpData);
+ clientExtensions[ExtensionType.srp] = srpData.ToArray();
+
+ return clientExtensions;
+ }
+
+ public virtual CompressionMethod[] GetCompressionMethods()
+ {
+ return new CompressionMethod[] { CompressionMethod.NULL };
+ }
+
+ public virtual void NotifySessionID(byte[] sessionID)
+ {
+ // Currently ignored
+ }
+
+ public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite)
+ {
+ this.selectedCipherSuite = selectedCipherSuite;
+ }
+
+ public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod)
+ {
+ this.selectedCompressionMethod = selectedCompressionMethod;
+ }
+
+ public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+ {
+ if (!secureRenegotiation)
+ {
+ /*
+ * RFC 5746 3.4. If the extension is not present, the server does not support
+ * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
+ * some clients may want to terminate the handshake instead of continuing; see
+ * Section 4.1 for discussion.
+ */
+// throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+ {
+ // There is no server response for the SRP extension
+ }
+
+ public virtual TlsKeyExchange GetKeyExchange()
+ {
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP);
+
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
+
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocolHandler verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher
+ * suites, so if we now can't produce an implementation, we shouldn't have
+ * offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public abstract TlsAuthentication GetAuthentication();
+
+ public virtual TlsCompression GetCompression()
+ {
+ switch (selectedCompressionMethod)
+ {
+ case CompressionMethod.NULL:
+ return new TlsNullCompression();
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocolHandler verifies that the
+ * server-selected compression method was in the list of client-offered compression
+ * methods, so if we now can't produce an implementation, we shouldn't have
+ * offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual TlsCipher GetCipher()
+ {
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocolHandler verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher
+ * suites, so if we now can't produce an implementation, we shouldn't have
+ * offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected virtual TlsKeyExchange CreateSrpKeyExchange(KeyExchangeAlgorithm keyExchange)
+ {
+ return new TlsSrpKeyExchange(context, keyExchange, identity, password);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/Ssl3Mac.cs b/Crypto/src/crypto/tls/Ssl3Mac.cs
new file mode 100644
index 000000000..b2f3f309e
--- /dev/null
+++ b/Crypto/src/crypto/tls/Ssl3Mac.cs
@@ -0,0 +1,114 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /**
+ * HMAC implementation based on original internet draft for HMAC (RFC 2104)
+ *
+ * The difference is that padding is concatentated versus XORed with the key
+ *
+ * H(K + opad, H(K + ipad, text))
+ */
+ public class Ssl3Mac
+ : IMac
+ {
+ private const byte IPAD = 0x36;
+ private const byte OPAD = 0x5C;
+
+ internal static readonly byte[] MD5_IPAD = GenPad(IPAD, 48);
+ internal static readonly byte[] MD5_OPAD = GenPad(OPAD, 48);
+ internal static readonly byte[] SHA1_IPAD = GenPad(IPAD, 40);
+ internal static readonly byte[] SHA1_OPAD = GenPad(OPAD, 40);
+
+ private IDigest digest;
+
+ private byte[] secret;
+ private byte[] ipad, opad;
+
+ /**
+ * Base constructor for one of the standard digest algorithms that the byteLength of
+ * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1.
+ *
+ * @param digest the digest.
+ */
+ public Ssl3Mac(IDigest digest)
+ {
+ this.digest = digest;
+
+ if (digest.GetDigestSize() == 20)
+ {
+ this.ipad = SHA1_IPAD;
+ this.opad = SHA1_OPAD;
+ }
+ else
+ {
+ this.ipad = MD5_IPAD;
+ this.opad = MD5_OPAD;
+ }
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "/SSL3MAC"; }
+ }
+
+ public virtual void Init(ICipherParameters parameters)
+ {
+ secret = Arrays.Clone(((KeyParameter)parameters).GetKey());
+
+ Reset();
+ }
+
+ public virtual int GetMacSize()
+ {
+ return digest.GetDigestSize();
+ }
+
+ public virtual void Update(byte input)
+ {
+ digest.Update(input);
+ }
+
+ public virtual void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ digest.BlockUpdate(input, inOff, len);
+ }
+
+ public virtual int DoFinal(byte[] output, int outOff)
+ {
+ byte[] tmp = new byte[digest.GetDigestSize()];
+ digest.DoFinal(tmp, 0);
+
+ digest.BlockUpdate(secret, 0, secret.Length);
+ digest.BlockUpdate(opad, 0, opad.Length);
+ digest.BlockUpdate(tmp, 0, tmp.Length);
+
+ int len = digest.DoFinal(output, outOff);
+
+ Reset();
+
+ return len;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public virtual void Reset()
+ {
+ digest.Reset();
+ digest.BlockUpdate(secret, 0, secret.Length);
+ digest.BlockUpdate(ipad, 0, ipad.Length);
+ }
+
+ private static byte[] GenPad(byte b, int count)
+ {
+ byte[] padding = new byte[count];
+ Arrays.Fill(padding, b);
+ return padding;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsAgreementCredentials.cs b/Crypto/src/crypto/tls/TlsAgreementCredentials.cs
new file mode 100644
index 000000000..46ee4f90e
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsAgreementCredentials.cs
@@ -0,0 +1,11 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsAgreementCredentials : TlsCredentials
+ {
+ /// <exception cref="IOException"></exception>
+ byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey);
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsAuthentication.cs b/Crypto/src/crypto/tls/TlsAuthentication.cs
new file mode 100644
index 000000000..9aea5e449
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsAuthentication.cs
@@ -0,0 +1,31 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsAuthentication
+ {
+ /// <summary>
+ /// Called by the protocol handler to report the server certificate.
+ /// </summary>
+ /// <remarks>
+ /// This method is responsible for certificate verification and validation
+ /// </remarks>
+ /// <param name="serverCertificate">The server <see cref="Certificate"/> received</param>
+ /// <exception cref="IOException"></exception>
+ void NotifyServerCertificate(Certificate serverCertificate);
+
+ /// <summary>
+ /// Return client credentials in response to server's certificate request
+ /// </summary>
+ /// <param name="certificateRequest">
+ /// A <see cref="CertificateRequest"/> containing server certificate request details
+ /// </param>
+ /// <returns>
+ /// A <see cref="TlsCredentials"/> to be used for client authentication
+ /// (or <c>null</c> for no client authentication)
+ /// </returns>
+ /// <exception cref="IOException"></exception>
+ TlsCredentials GetClientCredentials(CertificateRequest certificateRequest);
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsBlockCipher.cs b/Crypto/src/crypto/tls/TlsBlockCipher.cs
new file mode 100644
index 000000000..ef7be1913
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsBlockCipher.cs
@@ -0,0 +1,248 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// A generic TLS 1.0 block cipher. This can be used for AES or 3DES for example.
+ /// </summary>
+ public class TlsBlockCipher
+ : TlsCipher
+ {
+ protected TlsClientContext context;
+
+ protected IBlockCipher encryptCipher;
+ protected IBlockCipher decryptCipher;
+
+ protected TlsMac wMac;
+ protected TlsMac rMac;
+
+ public virtual TlsMac WriteMac
+ {
+ get { return wMac; }
+ }
+
+ public virtual TlsMac ReadMac
+ {
+ get { return rMac; }
+ }
+
+ public TlsBlockCipher(TlsClientContext context, IBlockCipher encryptCipher,
+ IBlockCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize)
+ {
+ this.context = context;
+ this.encryptCipher = encryptCipher;
+ this.decryptCipher = decryptCipher;
+
+ int prfSize = (2 * cipherKeySize) + writeDigest.GetDigestSize()
+ + readDigest.GetDigestSize() + encryptCipher.GetBlockSize()
+ + decryptCipher.GetBlockSize();
+
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion",
+ TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom),
+ prfSize);
+
+ int offset = 0;
+
+ // Init MACs
+ wMac = CreateTlsMac(writeDigest, keyBlock, ref offset);
+ rMac = CreateTlsMac(readDigest, keyBlock, ref offset);
+
+ // Build keys
+ KeyParameter encryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
+ KeyParameter decryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
+
+ // Add IVs
+ ParametersWithIV encryptParams = CreateParametersWithIV(encryptKey,
+ keyBlock, ref offset, encryptCipher.GetBlockSize());
+ ParametersWithIV decryptParams = CreateParametersWithIV(decryptKey,
+ keyBlock, ref offset, decryptCipher.GetBlockSize());
+
+ if (offset != prfSize)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ // Init Ciphers
+ encryptCipher.Init(true, encryptParams);
+ decryptCipher.Init(false, decryptParams);
+ }
+
+ protected virtual TlsMac CreateTlsMac(IDigest digest, byte[] buf, ref int off)
+ {
+ int len = digest.GetDigestSize();
+ TlsMac mac = new TlsMac(digest, buf, off, len);
+ off += len;
+ return mac;
+ }
+
+ protected virtual KeyParameter CreateKeyParameter(byte[] buf, ref int off, int len)
+ {
+ KeyParameter key = new KeyParameter(buf, off, len);
+ off += len;
+ return key;
+ }
+
+ protected virtual ParametersWithIV CreateParametersWithIV(KeyParameter key,
+ byte[] buf, ref int off, int len)
+ {
+ ParametersWithIV ivParams = new ParametersWithIV(key, buf, off, len);
+ off += len;
+ return ivParams;
+ }
+
+ public virtual byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len)
+ {
+ int blocksize = encryptCipher.GetBlockSize();
+
+ // Add a random number of extra blocks worth of padding
+ int minPaddingSize = blocksize - ((len + wMac.Size + 1) % blocksize);
+ int maxExtraPadBlocks = (255 - minPaddingSize) / blocksize;
+ int actualExtraPadBlocks = ChooseExtraPadBlocks(context.SecureRandom, maxExtraPadBlocks);
+ int paddingsize = minPaddingSize + (actualExtraPadBlocks * blocksize);
+
+ int totalsize = len + wMac.Size + paddingsize + 1;
+ byte[] outbuf = new byte[totalsize];
+ Array.Copy(plaintext, offset, outbuf, 0, len);
+ byte[] mac = wMac.CalculateMac(type, plaintext, offset, len);
+ Array.Copy(mac, 0, outbuf, len, mac.Length);
+ int paddoffset = len + mac.Length;
+ for (int i = 0; i <= paddingsize; i++)
+ {
+ outbuf[i + paddoffset] = (byte)paddingsize;
+ }
+ for (int i = 0; i < totalsize; i += blocksize)
+ {
+ encryptCipher.ProcessBlock(outbuf, i, outbuf, i);
+ }
+ return outbuf;
+ }
+
+ public virtual byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len)
+ {
+ // TODO TLS 1.1 (RFC 4346) introduces an explicit IV
+
+ int minLength = rMac.Size + 1;
+ int blocksize = decryptCipher.GetBlockSize();
+ bool decrypterror = false;
+
+ /*
+ * ciphertext must be at least (macsize + 1) bytes long
+ */
+ if (len < minLength)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ /*
+ * ciphertext must be a multiple of blocksize
+ */
+ if (len % blocksize != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decryption_failed);
+ }
+
+ /*
+ * Decrypt all the ciphertext using the blockcipher
+ */
+ for (int i = 0; i < len; i += blocksize)
+ {
+ decryptCipher.ProcessBlock(ciphertext, i + offset, ciphertext, i + offset);
+ }
+
+ /*
+ * Check if padding is correct
+ */
+ int lastByteOffset = offset + len - 1;
+
+ byte paddingsizebyte = ciphertext[lastByteOffset];
+
+ int paddingsize = paddingsizebyte;
+
+ int maxPaddingSize = len - minLength;
+ if (paddingsize > maxPaddingSize)
+ {
+ decrypterror = true;
+ paddingsize = 0;
+ }
+ else
+ {
+ /*
+ * Now, check all the padding-bytes (constant-time comparison).
+ */
+ byte diff = 0;
+ for (int i = lastByteOffset - paddingsize; i < lastByteOffset; ++i)
+ {
+ diff |= (byte)(ciphertext[i] ^ paddingsizebyte);
+ }
+ if (diff != 0)
+ {
+ /* Wrong padding */
+ decrypterror = true;
+ paddingsize = 0;
+ }
+ }
+
+ /*
+ * We now don't care if padding verification has failed or not, we will calculate
+ * the mac to give an attacker no kind of timing profile he can use to find out if
+ * mac verification failed or padding verification failed.
+ */
+ int plaintextlength = len - minLength - paddingsize;
+ byte[] calculatedMac = rMac.CalculateMac(type, ciphertext, offset, plaintextlength);
+
+ /*
+ * Check all bytes in the mac (constant-time comparison).
+ */
+ byte[] decryptedMac = new byte[calculatedMac.Length];
+ Array.Copy(ciphertext, offset + plaintextlength, decryptedMac, 0, calculatedMac.Length);
+
+ if (!Arrays.ConstantTimeAreEqual(calculatedMac, decryptedMac))
+ {
+ decrypterror = true;
+ }
+
+ /*
+ * Now, it is safe to fail.
+ */
+ if (decrypterror)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+
+ byte[] plaintext = new byte[plaintextlength];
+ Array.Copy(ciphertext, offset, plaintext, 0, plaintextlength);
+ return plaintext;
+ }
+
+ protected virtual int ChooseExtraPadBlocks(SecureRandom r, int max)
+ {
+// return r.NextInt(max + 1);
+
+ uint x = (uint)r.NextInt();
+ int n = LowestBitSet(x);
+ return System.Math.Min(n, max);
+ }
+
+ private int LowestBitSet(uint x)
+ {
+ if (x == 0)
+ {
+ return 32;
+ }
+
+ int n = 0;
+ while ((x & 1) == 0)
+ {
+ ++n;
+ x >>= 1;
+ }
+ return n;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsCipher.cs b/Crypto/src/crypto/tls/TlsCipher.cs
new file mode 100644
index 000000000..22c769d82
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsCipher.cs
@@ -0,0 +1,14 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsCipher
+ {
+ /// <exception cref="IOException"></exception>
+ byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len);
+
+ /// <exception cref="IOException"></exception>
+ byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len);
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsCipherFactory.cs b/Crypto/src/crypto/tls/TlsCipherFactory.cs
new file mode 100644
index 000000000..0756603f4
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsCipherFactory.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsCipherFactory
+ {
+ /// <exception cref="IOException"></exception>
+ TlsCipher CreateCipher(TlsClientContext context, EncryptionAlgorithm encryptionAlgorithm,
+ DigestAlgorithm digestAlgorithm);
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsClient.cs b/Crypto/src/crypto/tls/TlsClient.cs
new file mode 100644
index 000000000..eceaa3cd3
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsClient.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsClient
+ {
+ /// <summary>
+ /// Called at the start of a new TLS session, before any other methods.
+ /// </summary>
+ /// <param name="context">
+ /// A <see cref="TlsProtocolHandler"/>
+ /// </param>
+ void Init(TlsClientContext context);
+
+ /// <summary>
+ /// Get the list of cipher suites that this client supports.
+ /// </summary>
+ /// <returns>
+ /// An array of <see cref="CipherSuite"/>, each specifying a supported cipher suite.
+ /// </returns>
+ CipherSuite[] GetCipherSuites();
+
+ /// <summary>
+ /// Get the list of compression methods that this client supports.
+ /// </summary>
+ /// <returns>
+ /// An array of <see cref="CompressionMethod"/>, each specifying a supported compression method.
+ /// </returns>
+ CompressionMethod[] GetCompressionMethods();
+
+ /// <summary>
+ /// Get the (optional) table of client extensions to be included in (extended) client hello.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="IDictionary"/> (<see cref="ExtensionType"/> -> byte[]). May be null.
+ /// </returns>
+ /// <exception cref="IOException"></exception>
+ IDictionary GetClientExtensions();
+
+ /// <summary>
+ /// Reports the session ID once it has been determined.
+ /// </summary>
+ /// <param name="sessionID">
+ /// A <see cref="System.Byte"/>
+ /// </param>
+ void NotifySessionID(byte[] sessionID);
+
+ /// <summary>
+ /// Report the cipher suite that was selected by the server.
+ /// </summary>
+ /// <remarks>
+ /// The protocol handler validates this value against the offered cipher suites
+ /// <seealso cref="GetCipherSuites"/>
+ /// </remarks>
+ /// <param name="selectedCipherSuite">
+ /// A <see cref="CipherSuite"/>
+ /// </param>
+ void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite);
+
+ /// <summary>
+ /// Report the compression method that was selected by the server.
+ /// </summary>
+ /// <remarks>
+ /// The protocol handler validates this value against the offered compression methods
+ /// <seealso cref="GetCompressionMethods"/>
+ /// </remarks>
+ /// <param name="selectedCompressionMethod">
+ /// A <see cref="CompressionMethod"/>
+ /// </param>
+ void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod);
+
+ /// <summary>
+ /// Report whether the server supports secure renegotiation
+ /// </summary>
+ /// <remarks>
+ /// The protocol handler automatically processes the relevant extensions
+ /// </remarks>
+ /// <param name="secureRenegotiation">
+ /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation
+ /// </param>
+ /// <exception cref="IOException"></exception>
+ void NotifySecureRenegotiation(bool secureRenegotiation);
+
+ /// <summary>
+ /// Report the extensions from an extended server hello.
+ /// </summary>
+ /// <remarks>
+ /// Will only be called if we returned a non-null result from <see cref="GetClientExtensions"/>.
+ /// </remarks>
+ /// <param name="serverExtensions">
+ /// A <see cref="IDictionary"/> (<see cref="ExtensionType"/> -> byte[])
+ /// </param>
+ void ProcessServerExtensions(IDictionary serverExtensions);
+
+ /// <summary>
+ /// Return an implementation of <see cref="TlsKeyExchange"/> to negotiate the key exchange
+ /// part of the protocol.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="TlsKeyExchange"/>
+ /// </returns>
+ /// <exception cref="IOException"/>
+ TlsKeyExchange GetKeyExchange();
+
+ /// <summary>
+ /// Return an implementation of <see cref="TlsAuthentication"/> to handle authentication
+ /// part of the protocol.
+ /// </summary>
+ /// <exception cref="IOException"/>
+ TlsAuthentication GetAuthentication();
+
+ /// <summary>
+ /// Return an implementation of <see cref="TlsCompression"/> to handle record compression.
+ /// </summary>
+ /// <exception cref="IOException"/>
+ TlsCompression GetCompression();
+
+ /// <summary>
+ /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="TlsCipher"/>
+ /// </returns>
+ /// <exception cref="IOException"/>
+ TlsCipher GetCipher();
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsClientContext.cs b/Crypto/src/crypto/tls/TlsClientContext.cs
new file mode 100644
index 000000000..dbb10aa76
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsClientContext.cs
@@ -0,0 +1,15 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsClientContext
+ {
+ SecureRandom SecureRandom { get; }
+
+ SecurityParameters SecurityParameters { get; }
+
+ object UserObject { get; set; }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsClientContextImpl.cs b/Crypto/src/crypto/tls/TlsClientContextImpl.cs
new file mode 100644
index 000000000..9d5dee232
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsClientContextImpl.cs
@@ -0,0 +1,37 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsClientContextImpl
+ : TlsClientContext
+ {
+ private readonly SecureRandom secureRandom;
+ private readonly SecurityParameters securityParameters;
+
+ private object userObject = null;
+
+ internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
+ {
+ this.secureRandom = secureRandom;
+ this.securityParameters = securityParameters;
+ }
+
+ public virtual SecureRandom SecureRandom
+ {
+ get { return secureRandom; }
+ }
+
+ public virtual SecurityParameters SecurityParameters
+ {
+ get { return securityParameters; }
+ }
+
+ public virtual object UserObject
+ {
+ get { return userObject; }
+ set { this.userObject = value; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsCompression.cs b/Crypto/src/crypto/tls/TlsCompression.cs
new file mode 100644
index 000000000..177d64b7e
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsCompression.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsCompression
+ {
+ Stream Compress(Stream output);
+
+ Stream Decompress(Stream output);
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsCredentials.cs b/Crypto/src/crypto/tls/TlsCredentials.cs
new file mode 100644
index 000000000..5c5f1c02e
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsCredentials.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsCredentials
+ {
+ Certificate Certificate { get; }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsDHKeyExchange.cs b/Crypto/src/crypto/tls/TlsDHKeyExchange.cs
new file mode 100644
index 000000000..40ac416e0
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDHKeyExchange.cs
@@ -0,0 +1,201 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// TLS 1.0 DH key exchange.
+ /// </summary>
+ internal class TlsDHKeyExchange
+ : TlsKeyExchange
+ {
+ protected TlsClientContext context;
+ protected KeyExchangeAlgorithm keyExchange;
+ protected TlsSigner tlsSigner;
+
+ protected AsymmetricKeyParameter serverPublicKey = null;
+ protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
+ protected TlsAgreementCredentials agreementCredentials;
+ protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+
+ internal TlsDHKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DH_RSA:
+ case KeyExchangeAlgorithm.DH_DSS:
+ this.tlsSigner = null;
+ break;
+ case KeyExchangeAlgorithm.DHE_RSA:
+ this.tlsSigner = new TlsRsaSigner();
+ break;
+ case KeyExchangeAlgorithm.DHE_DSS:
+ this.tlsSigner = new TlsDssSigner();
+ break;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+
+ this.context = context;
+ this.keyExchange = keyExchange;
+ }
+
+ public virtual void SkipServerCertificate()
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ X509CertificateStructure x509Cert = serverCertificate.certs[0];
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+
+ try
+ {
+ this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+ }
+ catch (Exception)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ if (tlsSigner == null)
+ {
+ try
+ {
+ this.dhAgreeServerPublicKey = ValidateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
+ }
+ catch (InvalidCastException)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement);
+ }
+ else
+ {
+ if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+ }
+
+ // TODO
+ /*
+ * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
+ * signing algorithm for the certificate must be the same as the algorithm for the
+ * certificate key."
+ */
+ }
+
+ public virtual void SkipServerKeyExchange()
+ {
+ // OK
+ }
+
+ public virtual void ProcessServerKeyExchange(Stream input)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ {
+ ClientCertificateType[] types = certificateRequest.CertificateTypes;
+ foreach (ClientCertificateType type in types)
+ {
+ switch (type)
+ {
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.rsa_fixed_dh:
+ case ClientCertificateType.dss_fixed_dh:
+ case ClientCertificateType.ecdsa_sign:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public virtual void SkipClientCredentials()
+ {
+ this.agreementCredentials = null;
+ }
+
+ public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ if (clientCredentials is TlsAgreementCredentials)
+ {
+ // TODO Validate client cert has matching parameters (see 'areCompatibleParameters')?
+
+ this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+ }
+ else if (clientCredentials is TlsSignerCredentials)
+ {
+ // OK
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual void GenerateClientKeyExchange(Stream output)
+ {
+ /*
+ * RFC 2246 7.4.7.2 If the client certificate already contains a suitable
+ * Diffie-Hellman key, then Yc is implicit and does not need to be sent again. In
+ * this case, the Client Key Exchange message will be sent, but will be empty.
+ */
+ if (agreementCredentials == null)
+ {
+ GenerateEphemeralClientKeyExchange(dhAgreeServerPublicKey.Parameters, output);
+ }
+ }
+
+ public virtual byte[] GeneratePremasterSecret()
+ {
+ if (agreementCredentials != null)
+ {
+ return agreementCredentials.GenerateAgreement(dhAgreeServerPublicKey);
+ }
+
+ return CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+ }
+
+ protected virtual bool AreCompatibleParameters(DHParameters a, DHParameters b)
+ {
+ return a.P.Equals(b.P) && a.G.Equals(b.G);
+ }
+
+ protected virtual byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey,
+ DHPrivateKeyParameters privateKey)
+ {
+ return TlsDHUtilities.CalculateDHBasicAgreement(publicKey, privateKey);
+ }
+
+ protected virtual AsymmetricCipherKeyPair GenerateDHKeyPair(DHParameters dhParams)
+ {
+ return TlsDHUtilities.GenerateDHKeyPair(context.SecureRandom, dhParams);
+ }
+
+ protected virtual void GenerateEphemeralClientKeyExchange(DHParameters dhParams, Stream output)
+ {
+ this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(
+ context.SecureRandom, dhParams, output);
+ }
+
+ protected virtual DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key)
+ {
+ return TlsDHUtilities.ValidateDHPublicKey(key);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsDHUtilities.cs b/Crypto/src/crypto/tls/TlsDHUtilities.cs
new file mode 100644
index 000000000..733749ea1
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDHUtilities.cs
@@ -0,0 +1,70 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class TlsDHUtilities
+ {
+ public static byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey,
+ DHPrivateKeyParameters privateKey)
+ {
+ DHBasicAgreement dhAgree = new DHBasicAgreement();
+ dhAgree.Init(privateKey);
+ BigInteger agreement = dhAgree.CalculateAgreement(publicKey);
+ return BigIntegers.AsUnsignedByteArray(agreement);
+ }
+
+ public static AsymmetricCipherKeyPair GenerateDHKeyPair(SecureRandom random, DHParameters dhParams)
+ {
+ DHBasicKeyPairGenerator dhGen = new DHBasicKeyPairGenerator();
+ dhGen.Init(new DHKeyGenerationParameters(random, dhParams));
+ return dhGen.GenerateKeyPair();
+ }
+
+ public static DHPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random,
+ DHParameters dhParams, Stream output)
+ {
+ AsymmetricCipherKeyPair dhAgreeClientKeyPair = GenerateDHKeyPair(random, dhParams);
+ DHPrivateKeyParameters dhAgreeClientPrivateKey =
+ (DHPrivateKeyParameters)dhAgreeClientKeyPair.Private;
+
+ BigInteger Yc = ((DHPublicKeyParameters)dhAgreeClientKeyPair.Public).Y;
+ byte[] keData = BigIntegers.AsUnsignedByteArray(Yc);
+ TlsUtilities.WriteOpaque16(keData, output);
+
+ return dhAgreeClientPrivateKey;
+ }
+
+ public static DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key)
+ {
+ BigInteger Y = key.Y;
+ DHParameters parameters = key.Parameters;
+ BigInteger p = parameters.P;
+ BigInteger g = parameters.G;
+
+ if (!p.IsProbablePrime(2))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ if (g.CompareTo(BigInteger.Two) < 0 || g.CompareTo(p.Subtract(BigInteger.Two)) > 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ if (Y.CompareTo(BigInteger.Two) < 0 || Y.CompareTo(p.Subtract(BigInteger.One)) > 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ // TODO See RFC 2631 for more discussion of Diffie-Hellman validation
+
+ return key;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/tls/TlsDeflateCompression.cs b/Crypto/src/crypto/tls/TlsDeflateCompression.cs
new file mode 100644
index 000000000..146c961c7
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDeflateCompression.cs
@@ -0,0 +1,45 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.Zlib;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsDeflateCompression
+ : TlsCompression
+ {
+ protected ZStream zIn, zOut;
+
+ public TlsDeflateCompression()
+ {
+ this.zIn = new ZStream();
+ this.zIn.inflateInit();
+
+ this.zOut = new ZStream();
+ // TODO Allow custom setting
+ this.zOut.deflateInit(JZlib.Z_DEFAULT_COMPRESSION);
+ }
+
+ public virtual Stream Compress(Stream output)
+ {
+ return new DeflateOutputStream(output, zOut, true);
+ }
+
+ public virtual Stream Decompress(Stream output)
+ {
+ return new DeflateOutputStream(output, zIn, false);
+ }
+
+ protected class DeflateOutputStream : ZOutputStream
+ {
+ public DeflateOutputStream(Stream output, ZStream z, bool compress)
+ : base(output)
+ {
+ this.z = z;
+ this.compress = compress;
+ // TODO http://www.bolet.org/~pornin/deflate-flush.html says we should use Z_SYNC_FLUSH
+ this.FlushMode = JZlib.Z_PARTIAL_FLUSH;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsDheKeyExchange.cs b/Crypto/src/crypto/tls/TlsDheKeyExchange.cs
new file mode 100644
index 000000000..edadaeb38
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDheKeyExchange.cs
@@ -0,0 +1,56 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsDheKeyExchange
+ : TlsDHKeyExchange
+ {
+ internal TlsDheKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange)
+ : base(context, keyExchange)
+ {
+ }
+
+ public override void SkipServerKeyExchange()
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ ISigner signer = InitSigner(tlsSigner, securityParameters);
+ Stream sigIn = new SignerStream(input, signer, null);
+
+ byte[] pBytes = TlsUtilities.ReadOpaque16(sigIn);
+ byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn);
+ byte[] YsBytes = TlsUtilities.ReadOpaque16(sigIn);
+
+ byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+ if (!signer.VerifySignature(sigByte))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ BigInteger p = new BigInteger(1, pBytes);
+ BigInteger g = new BigInteger(1, gBytes);
+ BigInteger Ys = new BigInteger(1, YsBytes);
+
+ this.dhAgreeServerPublicKey = ValidateDHPublicKey(
+ new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+ }
+
+ protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ {
+ ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+ signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+ signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+ return signer;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsDsaSigner.cs b/Crypto/src/crypto/tls/TlsDsaSigner.cs
new file mode 100644
index 000000000..27d7b1f91
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDsaSigner.cs
@@ -0,0 +1,51 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal abstract class TlsDsaSigner
+ : TlsSigner
+ {
+ public virtual byte[] CalculateRawSignature(SecureRandom random,
+ AsymmetricKeyParameter privateKey, byte[] md5andsha1)
+ {
+ ISigner s = MakeSigner(new NullDigest(), true, new ParametersWithRandom(privateKey, random));
+ // Note: Only use the SHA1 part of the hash
+ s.BlockUpdate(md5andsha1, 16, 20);
+ return s.GenerateSignature();
+ }
+
+ public bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1)
+ {
+ ISigner s = MakeSigner(new NullDigest(), false, publicKey);
+ // Note: Only use the SHA1 part of the hash
+ s.BlockUpdate(md5andsha1, 16, 20);
+ return s.VerifySignature(sigBytes);
+ }
+
+ public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey)
+ {
+ return MakeSigner(new Sha1Digest(), true, new ParametersWithRandom(privateKey, random));
+ }
+
+ public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey)
+ {
+ return MakeSigner(new Sha1Digest(), false, publicKey);
+ }
+
+ public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey);
+
+ protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp)
+ {
+ ISigner s = new DsaDigestSigner(CreateDsaImpl(), d);
+ s.Init(forSigning, cp);
+ return s;
+ }
+
+ protected abstract IDsa CreateDsaImpl();
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsDssSigner.cs b/Crypto/src/crypto/tls/TlsDssSigner.cs
new file mode 100644
index 000000000..c6f1abcec
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDssSigner.cs
@@ -0,0 +1,21 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsDssSigner
+ : TlsDsaSigner
+ {
+ public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+ {
+ return publicKey is DsaPublicKeyParameters;
+ }
+
+ protected override IDsa CreateDsaImpl()
+ {
+ return new DsaSigner();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/Crypto/src/crypto/tls/TlsECDHKeyExchange.cs
new file mode 100644
index 000000000..83983ba47
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsECDHKeyExchange.cs
@@ -0,0 +1,230 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /**
+ * ECDH key exchange (see RFC 4492)
+ */
+ internal class TlsECDHKeyExchange
+ : TlsKeyExchange
+ {
+ protected TlsClientContext context;
+ protected KeyExchangeAlgorithm keyExchange;
+ protected TlsSigner tlsSigner;
+
+ protected AsymmetricKeyParameter serverPublicKey;
+ protected ECPublicKeyParameters ecAgreeServerPublicKey;
+ protected TlsAgreementCredentials agreementCredentials;
+ protected ECPrivateKeyParameters ecAgreeClientPrivateKey = null;
+
+ internal TlsECDHKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.ECDHE_RSA:
+ this.tlsSigner = new TlsRsaSigner();
+ break;
+ case KeyExchangeAlgorithm.ECDHE_ECDSA:
+ this.tlsSigner = new TlsECDsaSigner();
+ break;
+ case KeyExchangeAlgorithm.ECDH_RSA:
+ case KeyExchangeAlgorithm.ECDH_ECDSA:
+ this.tlsSigner = null;
+ break;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+
+ this.context = context;
+ this.keyExchange = keyExchange;
+ }
+
+ public virtual void SkipServerCertificate()
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ X509CertificateStructure x509Cert = serverCertificate.certs[0];
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+
+ try
+ {
+ this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+ }
+ catch (Exception)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ if (tlsSigner == null)
+ {
+ try
+ {
+ this.ecAgreeServerPublicKey = ValidateECPublicKey((ECPublicKeyParameters)this.serverPublicKey);
+ }
+ catch (InvalidCastException)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement);
+ }
+ else
+ {
+ if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+ }
+
+ // TODO
+ /*
+ * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
+ * signing algorithm for the certificate must be the same as the algorithm for the
+ * certificate key."
+ */
+ }
+
+ public virtual void SkipServerKeyExchange()
+ {
+ // do nothing
+ }
+
+ public virtual void ProcessServerKeyExchange(Stream input)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ {
+ /*
+ * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable
+ * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is
+ * prohibited because the use of a long-term ECDH client key would jeopardize the
+ * forward secrecy property of these algorithms.
+ */
+ ClientCertificateType[] types = certificateRequest.CertificateTypes;
+ foreach (ClientCertificateType type in types)
+ {
+ switch (type)
+ {
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ case ClientCertificateType.rsa_fixed_ecdh:
+ case ClientCertificateType.ecdsa_fixed_ecdh:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public virtual void SkipClientCredentials()
+ {
+ this.agreementCredentials = null;
+ }
+
+ public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ if (clientCredentials is TlsAgreementCredentials)
+ {
+ // TODO Validate client cert has matching parameters (see 'AreOnSameCurve')?
+
+ this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+ }
+ else if (clientCredentials is TlsSignerCredentials)
+ {
+ // OK
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual void GenerateClientKeyExchange(Stream output)
+ {
+ if (agreementCredentials == null)
+ {
+ GenerateEphemeralClientKeyExchange(ecAgreeServerPublicKey.Parameters, output);
+ }
+ }
+
+ public virtual byte[] GeneratePremasterSecret()
+ {
+ if (agreementCredentials != null)
+ {
+ return agreementCredentials.GenerateAgreement(ecAgreeServerPublicKey);
+ }
+
+ return CalculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey);
+ }
+
+ protected virtual bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b)
+ {
+ // TODO Move to ECDomainParameters.Equals() or other utility method?
+ return a.Curve.Equals(b.Curve) && a.G.Equals(b.G) && a.N.Equals(b.N) && a.H.Equals(b.H);
+ }
+
+ protected virtual byte[] ExternalizeKey(ECPublicKeyParameters keyParameters)
+ {
+ // TODO Add support for compressed encoding and SPF extension
+
+ /*
+ * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format.
+ * Here, the format MUST conform to what the server has requested through a
+ * Supported Point Formats Extension if this extension was used, and MUST be
+ * uncompressed if this extension was not used.
+ */
+ return keyParameters.Q.GetEncoded();
+ }
+
+ protected virtual AsymmetricCipherKeyPair GenerateECKeyPair(ECDomainParameters ecParams)
+ {
+ ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+ ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams,
+ context.SecureRandom);
+ keyPairGenerator.Init(keyGenerationParameters);
+ return keyPairGenerator.GenerateKeyPair();
+ }
+
+ protected virtual void GenerateEphemeralClientKeyExchange(ECDomainParameters ecParams, Stream output)
+ {
+ AsymmetricCipherKeyPair ecAgreeClientKeyPair = GenerateECKeyPair(ecParams);
+ this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.Private;
+
+ byte[] keData = ExternalizeKey((ECPublicKeyParameters)ecAgreeClientKeyPair.Public);
+ TlsUtilities.WriteOpaque8(keData, output);
+ }
+
+ protected virtual byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey,
+ ECPrivateKeyParameters privateKey)
+ {
+ ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
+ basicAgreement.Init(privateKey);
+ BigInteger agreement = basicAgreement.CalculateAgreement(publicKey);
+ return BigIntegers.AsUnsignedByteArray(agreement);
+ }
+
+ protected virtual ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key)
+ {
+ // TODO Check RFC 4492 for validation
+ return key;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/Crypto/src/crypto/tls/TlsECDheKeyExchange.cs
new file mode 100644
index 000000000..5516154ce
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /**
+ * ECDHE key exchange (see RFC 4492)
+ */
+ internal class TlsECDheKeyExchange : TlsECDHKeyExchange
+ {
+ internal TlsECDheKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange)
+ : base(context, keyExchange)
+ {
+ }
+
+ public override void SkipServerKeyExchange()
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public override void ProcessServerKeyExchange(Stream input)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ ISigner signer = InitSigner(tlsSigner, securityParameters);
+ Stream sigIn = new SignerStream(input, signer, null);
+
+ ECCurveType curveType = (ECCurveType)TlsUtilities.ReadUint8(sigIn);
+ ECDomainParameters curve_params;
+
+ // Currently, we only support named curves
+ if (curveType == ECCurveType.named_curve)
+ {
+ NamedCurve namedCurve = (NamedCurve)TlsUtilities.ReadUint16(sigIn);
+
+ // TODO Check namedCurve is one we offered?
+
+ curve_params = NamedCurveHelper.GetECParameters(namedCurve);
+ }
+ else
+ {
+ // TODO Add support for explicit curve parameters (read from sigIn)
+
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ byte[] publicBytes = TlsUtilities.ReadOpaque8(sigIn);
+
+ byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+ if (!signer.VerifySignature(sigByte))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ // TODO Check curve_params not null
+
+ ECPoint Q = curve_params.Curve.DecodePoint(publicBytes);
+
+ this.ecAgreeServerPublicKey = ValidateECPublicKey(new ECPublicKeyParameters(Q, curve_params));
+ }
+
+ public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ {
+ /*
+ * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable
+ * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is
+ * prohibited because the use of a long-term ECDH client key would jeopardize the
+ * forward secrecy property of these algorithms.
+ */
+ ClientCertificateType[] types = certificateRequest.CertificateTypes;
+ foreach (ClientCertificateType type in types)
+ {
+ switch (type)
+ {
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ if (clientCredentials is TlsSignerCredentials)
+ {
+ // OK
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ {
+ ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+ signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+ signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+ return signer;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsECDsaSigner.cs b/Crypto/src/crypto/tls/TlsECDsaSigner.cs
new file mode 100644
index 000000000..3c30fdc0c
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsECDsaSigner.cs
@@ -0,0 +1,21 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsECDsaSigner
+ : TlsDsaSigner
+ {
+ public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+ {
+ return publicKey is ECPublicKeyParameters;
+ }
+
+ protected override IDsa CreateDsaImpl()
+ {
+ return new ECDsaSigner();
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsException.cs b/Crypto/src/crypto/tls/TlsException.cs
new file mode 100644
index 000000000..fa3e73273
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsException : Exception
+ {
+ public TlsException() : base() { }
+ public TlsException(string message) : base(message) { }
+ public TlsException(string message, Exception exception) : base(message, exception) { }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsFatalAlert.cs b/Crypto/src/crypto/tls/TlsFatalAlert.cs
new file mode 100644
index 000000000..0a9cc6f3a
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsFatalAlert.cs
@@ -0,0 +1,21 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsFatalAlert
+ : IOException
+ {
+ private readonly AlertDescription alertDescription;
+
+ public TlsFatalAlert(AlertDescription alertDescription)
+ {
+ this.alertDescription = alertDescription;
+ }
+
+ public AlertDescription AlertDescription
+ {
+ get { return alertDescription; }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsKeyExchange.cs b/Crypto/src/crypto/tls/TlsKeyExchange.cs
new file mode 100644
index 000000000..5102edbec
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsKeyExchange.cs
@@ -0,0 +1,38 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// A generic interface for key exchange implementations in TLS 1.0.
+ /// </summary>
+ public interface TlsKeyExchange
+ {
+ /// <exception cref="IOException"/>
+ void SkipServerCertificate();
+
+ /// <exception cref="IOException"/>
+ void ProcessServerCertificate(Certificate serverCertificate);
+
+ /// <exception cref="IOException"/>
+ void SkipServerKeyExchange();
+
+ /// <exception cref="IOException"/>
+ void ProcessServerKeyExchange(Stream input);
+
+ /// <exception cref="IOException"/>
+ void ValidateCertificateRequest(CertificateRequest certificateRequest);
+
+ /// <exception cref="IOException"/>
+ void SkipClientCredentials();
+
+ /// <exception cref="IOException"/>
+ void ProcessClientCredentials(TlsCredentials clientCredentials);
+
+ /// <exception cref="IOException"/>
+ void GenerateClientKeyExchange(Stream output);
+
+ /// <exception cref="IOException"/>
+ byte[] GeneratePremasterSecret();
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsMac.cs b/Crypto/src/crypto/tls/TlsMac.cs
new file mode 100644
index 000000000..0e58b89dc
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsMac.cs
@@ -0,0 +1,106 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <remarks>
+ /// A generic TLS MAC implementation, which can be used with any kind of
+ /// IDigest to act as an HMAC.
+ /// </remarks>
+ public class TlsMac
+ {
+ protected long seqNo;
+ protected byte[] secret;
+ protected HMac mac;
+
+ /**
+ * Generate a new instance of an TlsMac.
+ *
+ * @param digest The digest to use.
+ * @param key_block A byte-array where the key for this mac is located.
+ * @param offset The number of bytes to skip, before the key starts in the buffer.
+ * @param len The length of the key.
+ */
+ public TlsMac(
+ IDigest digest,
+ byte[] key_block,
+ int offset,
+ int len)
+ {
+ this.seqNo = 0;
+
+ KeyParameter param = new KeyParameter(key_block, offset, len);
+
+ this.secret = Arrays.Clone(param.GetKey());
+
+ this.mac = new HMac(digest);
+ this.mac.Init(param);
+ }
+
+ /**
+ * @return the MAC write secret
+ */
+ public virtual byte[] GetMacSecret()
+ {
+ return this.secret;
+ }
+
+ /**
+ * @return the current write sequence number
+ */
+ public virtual long SequenceNumber
+ {
+ get { return this.seqNo; }
+ }
+
+ /**
+ * Increment the current write sequence number
+ */
+ public virtual void IncSequenceNumber()
+ {
+ this.seqNo++;
+ }
+
+ /**
+ * @return The Keysize of the mac.
+ */
+ public virtual int Size
+ {
+ get { return mac.GetMacSize(); }
+ }
+
+ /**
+ * Calculate the mac for some given data.
+ * <p/>
+ * TlsMac will keep track of the sequence number internally.
+ *
+ * @param type The message type of the message.
+ * @param message A byte-buffer containing the message.
+ * @param offset The number of bytes to skip, before the message starts.
+ * @param len The length of the message.
+ * @return A new byte-buffer containing the mac value.
+ */
+ public virtual byte[] CalculateMac(
+ ContentType type,
+ byte[] message,
+ int offset,
+ int len)
+ {
+ byte[] macHeader = new byte[13];
+ TlsUtilities.WriteUint64(seqNo++, macHeader, 0);
+ TlsUtilities.WriteUint8((byte)type, macHeader, 8);
+ TlsUtilities.WriteVersion(macHeader, 9);
+ TlsUtilities.WriteUint16(len, macHeader, 11);
+
+ mac.BlockUpdate(macHeader, 0, macHeader.Length);
+ mac.BlockUpdate(message, offset, len);
+ return MacUtilities.DoFinal(mac);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsNullCipher.cs b/Crypto/src/crypto/tls/TlsNullCipher.cs
new file mode 100644
index 000000000..b76f76d9c
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsNullCipher.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// A NULL cipher suite, for use during handshake.
+ /// </summary>
+ public class TlsNullCipher
+ : TlsCipher
+ {
+ public virtual byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len)
+ {
+ return CopyData(plaintext, offset, len);
+ }
+
+ public virtual byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len)
+ {
+ return CopyData(ciphertext, offset, len);
+ }
+
+ protected virtual byte[] CopyData(byte[] text, int offset, int len)
+ {
+ byte[] result = new byte[len];
+ Array.Copy(text, offset, result, 0, len);
+ return result;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsNullCompression.cs b/Crypto/src/crypto/tls/TlsNullCompression.cs
new file mode 100644
index 000000000..45f8fc708
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsNullCompression.cs
@@ -0,0 +1,19 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsNullCompression
+ : TlsCompression
+ {
+ public virtual Stream Compress(Stream output)
+ {
+ return output;
+ }
+
+ public virtual Stream Decompress(Stream output)
+ {
+ return output;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsProtocolHandler.cs b/Crypto/src/crypto/tls/TlsProtocolHandler.cs
new file mode 100644
index 000000000..6d2b0b144
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -0,0 +1,1259 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <remarks>An implementation of all high level protocols in TLS 1.0.</remarks>
+ public class TlsProtocolHandler
+ {
+ /*
+ * Our Connection states
+ */
+ private const short CS_CLIENT_HELLO_SEND = 1;
+ private const short CS_SERVER_HELLO_RECEIVED = 2;
+ private const short CS_SERVER_CERTIFICATE_RECEIVED = 3;
+ private const short CS_SERVER_KEY_EXCHANGE_RECEIVED = 4;
+ private const short CS_CERTIFICATE_REQUEST_RECEIVED = 5;
+ private const short CS_SERVER_HELLO_DONE_RECEIVED = 6;
+ private const short CS_CLIENT_KEY_EXCHANGE_SEND = 7;
+ private const short CS_CERTIFICATE_VERIFY_SEND = 8;
+ private const short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 9;
+ private const short CS_CLIENT_FINISHED_SEND = 10;
+ private const short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 11;
+ private const short CS_DONE = 12;
+
+ private static readonly byte[] emptybuf = new byte[0];
+
+ private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
+
+ /*
+ * Queues for data from some protocols.
+ */
+
+ private ByteQueue applicationDataQueue = new ByteQueue();
+ private ByteQueue changeCipherSpecQueue = new ByteQueue();
+ private ByteQueue alertQueue = new ByteQueue();
+ private ByteQueue handshakeQueue = new ByteQueue();
+
+ /*
+ * The Record Stream we use
+ */
+ private RecordStream rs;
+ private SecureRandom random;
+
+ private TlsStream tlsStream = null;
+
+ private bool closed = false;
+ private bool failedWithError = false;
+ private bool appDataReady = false;
+ private IDictionary clientExtensions;
+
+ private SecurityParameters securityParameters = null;
+
+ private TlsClientContextImpl tlsClientContext = null;
+ private TlsClient tlsClient = null;
+ private CipherSuite[] offeredCipherSuites = null;
+ private CompressionMethod[] offeredCompressionMethods = null;
+ private TlsKeyExchange keyExchange = null;
+ private TlsAuthentication authentication = null;
+ private CertificateRequest certificateRequest = null;
+
+ private short connection_state = 0;
+
+ private static SecureRandom CreateSecureRandom()
+ {
+ /*
+ * We use our threaded seed generator to generate a good random seed. If the user
+ * has a better random seed, he should use the constructor with a SecureRandom.
+ *
+ * Hopefully, 20 bytes in fast mode are good enough.
+ */
+ byte[] seed = new ThreadedSeedGenerator().GenerateSeed(20, true);
+
+ return new SecureRandom(seed);
+ }
+
+ public TlsProtocolHandler(
+ Stream s)
+ : this(s, s)
+ {
+ }
+
+ public TlsProtocolHandler(
+ Stream s,
+ SecureRandom sr)
+ : this(s, s, sr)
+ {
+ }
+
+ /// <remarks>Both streams can be the same object</remarks>
+ public TlsProtocolHandler(
+ Stream inStr,
+ Stream outStr)
+ : this(inStr, outStr, CreateSecureRandom())
+ {
+ }
+
+ /// <remarks>Both streams can be the same object</remarks>
+ public TlsProtocolHandler(
+ Stream inStr,
+ Stream outStr,
+ SecureRandom sr)
+ {
+ this.rs = new RecordStream(this, inStr, outStr);
+ this.random = sr;
+ }
+
+ internal void ProcessData(
+ ContentType protocol,
+ byte[] buf,
+ int offset,
+ int len)
+ {
+ /*
+ * Have a look at the protocol type, and add it to the correct queue.
+ */
+ switch (protocol)
+ {
+ case ContentType.change_cipher_spec:
+ changeCipherSpecQueue.AddData(buf, offset, len);
+ ProcessChangeCipherSpec();
+ break;
+ case ContentType.alert:
+ alertQueue.AddData(buf, offset, len);
+ ProcessAlert();
+ break;
+ case ContentType.handshake:
+ handshakeQueue.AddData(buf, offset, len);
+ ProcessHandshake();
+ break;
+ case ContentType.application_data:
+ if (!appDataReady)
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+ applicationDataQueue.AddData(buf, offset, len);
+ ProcessApplicationData();
+ break;
+ default:
+ /*
+ * Uh, we don't know this protocol.
+ *
+ * RFC2246 defines on page 13, that we should ignore this.
+ */
+ break;
+ }
+ }
+
+ private void ProcessHandshake()
+ {
+ bool read;
+ do
+ {
+ read = false;
+
+ /*
+ * We need the first 4 bytes, they contain type and length of
+ * the message.
+ */
+ if (handshakeQueue.Available >= 4)
+ {
+ byte[] beginning = new byte[4];
+ handshakeQueue.Read(beginning, 0, 4, 0);
+ MemoryStream bis = new MemoryStream(beginning, false);
+ HandshakeType type = (HandshakeType)TlsUtilities.ReadUint8(bis);
+ int len = TlsUtilities.ReadUint24(bis);
+
+ /*
+ * Check if we have enough bytes in the buffer to read
+ * the full message.
+ */
+ if (handshakeQueue.Available >= (len + 4))
+ {
+ /*
+ * Read the message.
+ */
+ byte[] buf = new byte[len];
+ handshakeQueue.Read(buf, 0, len, 4);
+ handshakeQueue.RemoveData(len + 4);
+
+ /*
+ * RFC 2246 7.4.9. The value handshake_messages includes all
+ * handshake messages starting at client hello up to, but not
+ * including, this finished message. [..] Note: [Also,] Hello Request
+ * messages are omitted from handshake hashes.
+ */
+ switch (type)
+ {
+ case HandshakeType.hello_request:
+ case HandshakeType.finished:
+ break;
+ default:
+ rs.UpdateHandshakeData(beginning, 0, 4);
+ rs.UpdateHandshakeData(buf, 0, len);
+ break;
+ }
+
+ /*
+ * Now, parse the message.
+ */
+ ProcessHandshakeMessage(type, buf);
+ read = true;
+ }
+ }
+ }
+ while (read);
+ }
+
+ private void ProcessHandshakeMessage(HandshakeType type, byte[] buf)
+ {
+ MemoryStream inStr = new MemoryStream(buf, false);
+
+ /*
+ * Check the type.
+ */
+ switch (type)
+ {
+ case HandshakeType.certificate:
+ {
+ switch (connection_state)
+ {
+ case CS_SERVER_HELLO_RECEIVED:
+ {
+ // Parse the Certificate message and send to cipher suite
+
+ Certificate serverCertificate = Certificate.Parse(inStr);
+
+ AssertEmpty(inStr);
+
+ this.keyExchange.ProcessServerCertificate(serverCertificate);
+
+ this.authentication = tlsClient.GetAuthentication();
+ this.authentication.NotifyServerCertificate(serverCertificate);
+
+ break;
+ }
+ default:
+ this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ break;
+ }
+
+ connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
+ break;
+ }
+ case HandshakeType.finished:
+ switch (connection_state)
+ {
+ case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED:
+ /*
+ * Read the checksum from the finished message, it has always 12 bytes.
+ */
+ byte[] serverVerifyData = new byte[12];
+ TlsUtilities.ReadFully(serverVerifyData, inStr);
+
+ AssertEmpty(inStr);
+
+ /*
+ * Calculate our own checksum.
+ */
+ byte[] expectedServerVerifyData = TlsUtilities.PRF(
+ securityParameters.masterSecret, "server finished",
+ rs.GetCurrentHash(), 12);
+
+ /*
+ * Compare both checksums.
+ */
+ if (!Arrays.ConstantTimeAreEqual(expectedServerVerifyData, serverVerifyData))
+ {
+ /*
+ * Wrong checksum in the finished message.
+ */
+ this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+
+ connection_state = CS_DONE;
+
+ /*
+ * We are now ready to receive application data.
+ */
+ this.appDataReady = true;
+ break;
+ default:
+ this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ break;
+ }
+ break;
+ case HandshakeType.server_hello:
+ switch (connection_state)
+ {
+ case CS_CLIENT_HELLO_SEND:
+ /*
+ * Read the server hello message
+ */
+ TlsUtilities.CheckVersion(inStr);
+
+ /*
+ * Read the server random
+ */
+ securityParameters.serverRandom = new byte[32];
+ TlsUtilities.ReadFully(securityParameters.serverRandom, inStr);
+
+ byte[] sessionID = TlsUtilities.ReadOpaque8(inStr);
+ if (sessionID.Length > 32)
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ this.tlsClient.NotifySessionID(sessionID);
+
+ /*
+ * Find out which CipherSuite the server has chosen and check that
+ * it was one of the offered ones.
+ */
+ CipherSuite selectedCipherSuite = (CipherSuite)TlsUtilities.ReadUint16(inStr);
+ if (!ArrayContains(offeredCipherSuites, selectedCipherSuite)
+ || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ this.tlsClient.NotifySelectedCipherSuite(selectedCipherSuite);
+
+ /*
+ * Find out which CompressionMethod the server has chosen and check that
+ * it was one of the offered ones.
+ */
+ CompressionMethod selectedCompressionMethod = (CompressionMethod)TlsUtilities.ReadUint8(inStr);
+ if (!ArrayContains(offeredCompressionMethods, selectedCompressionMethod))
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ this.tlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod);
+
+ /*
+ * RFC3546 2.2 The extended server hello message format MAY be
+ * sent in place of the server hello message when the client has
+ * requested extended functionality via the extended client hello
+ * message specified in Section 2.1.
+ * ...
+ * Note that the extended server hello message is only sent in response
+ * to an extended client hello message. This prevents the possibility
+ * that the extended server hello message could "break" existing TLS 1.0
+ * clients.
+ */
+
+ /*
+ * TODO RFC 3546 2.3
+ * If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and send a server hello
+ * containing no extensions.
+ */
+
+ // ExtensionType -> byte[]
+ IDictionary serverExtensions = Platform.CreateHashtable();
+
+ if (inStr.Position < inStr.Length)
+ {
+ // Process extensions from extended server hello
+ byte[] extBytes = TlsUtilities.ReadOpaque16(inStr);
+
+ MemoryStream ext = new MemoryStream(extBytes, false);
+ while (ext.Position < ext.Length)
+ {
+ ExtensionType extType = (ExtensionType)TlsUtilities.ReadUint16(ext);
+ byte[] extValue = TlsUtilities.ReadOpaque16(ext);
+
+ // Note: RFC 5746 makes a special case for EXT_RenegotiationInfo
+ if (extType != ExtensionType.renegotiation_info
+ && !clientExtensions.Contains(extType))
+ {
+ /*
+ * RFC 3546 2.3
+ * Note that for all extension types (including those defined in
+ * future), the extension type MUST NOT appear in the extended server
+ * hello unless the same extension type appeared in the corresponding
+ * client hello. Thus clients MUST abort the handshake if they receive
+ * an extension type in the extended server hello that they did not
+ * request in the associated (extended) client hello.
+ */
+ this.FailWithError(AlertLevel.fatal, AlertDescription.unsupported_extension);
+ }
+
+ if (serverExtensions.Contains(extType))
+ {
+ /*
+ * RFC 3546 2.3
+ * Also note that when multiple extensions of different types are
+ * present in the extended client hello or the extended server hello,
+ * the extensions may appear in any order. There MUST NOT be more than
+ * one extension of the same type.
+ */
+ this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ }
+
+ serverExtensions.Add(extType, extValue);
+ }
+ }
+
+ AssertEmpty(inStr);
+
+ /*
+ * RFC 5746 3.4. When a ServerHello is received, the client MUST check if it
+ * includes the "renegotiation_info" extension:
+ */
+ {
+ bool secure_negotiation = serverExtensions.Contains(ExtensionType.renegotiation_info);
+
+ /*
+ * If the extension is present, set the secure_renegotiation flag
+ * to TRUE. The client MUST then verify that the length of the
+ * "renegotiated_connection" field is zero, and if it is not, MUST
+ * abort the handshake (by sending a fatal handshake_failure
+ * alert).
+ */
+ if (secure_negotiation)
+ {
+ byte[] renegExtValue = (byte[])serverExtensions[ExtensionType.renegotiation_info];
+
+ if (!Arrays.ConstantTimeAreEqual(renegExtValue,
+ CreateRenegotiationInfo(emptybuf)))
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+ }
+
+ tlsClient.NotifySecureRenegotiation(secure_negotiation);
+ }
+
+ if (clientExtensions != null)
+ {
+ tlsClient.ProcessServerExtensions(serverExtensions);
+ }
+
+ this.keyExchange = tlsClient.GetKeyExchange();
+
+ connection_state = CS_SERVER_HELLO_RECEIVED;
+ break;
+ default:
+ this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ break;
+ }
+ break;
+ case HandshakeType.server_hello_done:
+ switch (connection_state)
+ {
+ case CS_SERVER_HELLO_RECEIVED:
+ case CS_SERVER_CERTIFICATE_RECEIVED:
+ case CS_SERVER_KEY_EXCHANGE_RECEIVED:
+ case CS_CERTIFICATE_REQUEST_RECEIVED:
+
+ // NB: Original code used case label fall-through
+
+ if (connection_state == CS_SERVER_HELLO_RECEIVED)
+ {
+ // There was no server certificate message; check it's OK
+ this.keyExchange.SkipServerCertificate();
+ this.authentication = null;
+
+ // There was no server key exchange message; check it's OK
+ this.keyExchange.SkipServerKeyExchange();
+ }
+ else if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
+ {
+ // There was no server key exchange message; check it's OK
+ this.keyExchange.SkipServerKeyExchange();
+ }
+
+ AssertEmpty(inStr);
+
+ connection_state = CS_SERVER_HELLO_DONE_RECEIVED;
+
+ TlsCredentials clientCreds = null;
+ if (certificateRequest == null)
+ {
+ this.keyExchange.SkipClientCredentials();
+ }
+ else
+ {
+ clientCreds = this.authentication.GetClientCredentials(certificateRequest);
+
+ Certificate clientCert;
+ if (clientCreds == null)
+ {
+ this.keyExchange.SkipClientCredentials();
+ clientCert = Certificate.EmptyChain;
+ }
+ else
+ {
+ this.keyExchange.ProcessClientCredentials(clientCreds);
+ clientCert = clientCreds.Certificate;
+ }
+
+ SendClientCertificate(clientCert);
+ }
+
+ /*
+ * Send the client key exchange message, depending on the key
+ * exchange we are using in our CipherSuite.
+ */
+ SendClientKeyExchange();
+
+ connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;
+
+ if (clientCreds != null && clientCreds is TlsSignerCredentials)
+ {
+ TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds;
+ byte[] md5andsha1 = rs.GetCurrentHash();
+ byte[] clientCertificateSignature = signerCreds.GenerateCertificateSignature(
+ md5andsha1);
+ SendCertificateVerify(clientCertificateSignature);
+
+ connection_state = CS_CERTIFICATE_VERIFY_SEND;
+ }
+
+ /*
+ * Now, we send change cipher state
+ */
+ byte[] cmessage = new byte[1];
+ cmessage[0] = 1;
+ rs.WriteMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.Length);
+
+ connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;
+
+ /*
+ * Calculate the master_secret
+ */
+ byte[] pms = this.keyExchange.GeneratePremasterSecret();
+
+ securityParameters.masterSecret = TlsUtilities.PRF(pms, "master secret",
+ TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom),
+ 48);
+
+ // TODO Is there a way to ensure the data is really overwritten?
+ /*
+ * RFC 2246 8.1. The pre_master_secret should be deleted from
+ * memory once the master_secret has been computed.
+ */
+ Array.Clear(pms, 0, pms.Length);
+
+ /*
+ * Initialize our cipher suite
+ */
+ rs.ClientCipherSpecDecided(tlsClient.GetCompression(), tlsClient.GetCipher());
+
+ /*
+ * Send our finished message.
+ */
+ byte[] clientVerifyData = TlsUtilities.PRF(securityParameters.masterSecret,
+ "client finished", rs.GetCurrentHash(), 12);
+
+ MemoryStream bos = new MemoryStream();
+ TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos);
+ TlsUtilities.WriteOpaque24(clientVerifyData, bos);
+ byte[] message = bos.ToArray();
+
+ rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+
+ this.connection_state = CS_CLIENT_FINISHED_SEND;
+ break;
+ default:
+ this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ break;
+ }
+ break;
+ case HandshakeType.server_key_exchange:
+ {
+ switch (connection_state)
+ {
+ case CS_SERVER_HELLO_RECEIVED:
+ case CS_SERVER_CERTIFICATE_RECEIVED:
+ {
+ // NB: Original code used case label fall-through
+ if (connection_state == CS_SERVER_HELLO_RECEIVED)
+ {
+ // There was no server certificate message; check it's OK
+ this.keyExchange.SkipServerCertificate();
+ this.authentication = null;
+ }
+
+ this.keyExchange.ProcessServerKeyExchange(inStr);
+
+ AssertEmpty(inStr);
+ break;
+ }
+ default:
+ this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ break;
+ }
+
+ this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
+ break;
+ }
+ case HandshakeType.certificate_request:
+ switch (connection_state)
+ {
+ case CS_SERVER_CERTIFICATE_RECEIVED:
+ case CS_SERVER_KEY_EXCHANGE_RECEIVED:
+ {
+ // NB: Original code used case label fall-through
+ if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
+ {
+ // There was no server key exchange message; check it's OK
+ this.keyExchange.SkipServerKeyExchange();
+ }
+
+ if (this.authentication == null)
+ {
+ /*
+ * RFC 2246 7.4.4. It is a fatal handshake_failure alert
+ * for an anonymous server to request client identification.
+ */
+ this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+
+ int numTypes = TlsUtilities.ReadUint8(inStr);
+ ClientCertificateType[] certificateTypes = new ClientCertificateType[numTypes];
+ for (int i = 0; i < numTypes; ++i)
+ {
+ certificateTypes[i] = (ClientCertificateType)TlsUtilities.ReadUint8(inStr);
+ }
+
+ byte[] authorities = TlsUtilities.ReadOpaque16(inStr);
+
+ AssertEmpty(inStr);
+
+ IList authorityDNs = Platform.CreateArrayList();
+
+ MemoryStream bis = new MemoryStream(authorities, false);
+ while (bis.Position < bis.Length)
+ {
+ byte[] dnBytes = TlsUtilities.ReadOpaque16(bis);
+ // TODO Switch to X500Name when available
+ authorityDNs.Add(X509Name.GetInstance(Asn1Object.FromByteArray(dnBytes)));
+ }
+
+ this.certificateRequest = new CertificateRequest(certificateTypes,
+ authorityDNs);
+ this.keyExchange.ValidateCertificateRequest(this.certificateRequest);
+
+ break;
+ }
+ default:
+ this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ break;
+ }
+
+ this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED;
+ break;
+ case HandshakeType.hello_request:
+ /*
+ * RFC 2246 7.4.1.1 Hello request
+ * This message will be ignored by the client if the client is currently
+ * negotiating a session. This message may be ignored by the client if it
+ * does not wish to renegotiate a session, or the client may, if it wishes,
+ * respond with a no_renegotiation alert.
+ */
+ if (connection_state == CS_DONE)
+ {
+ // Renegotiation not supported yet
+ SendAlert(AlertLevel.warning, AlertDescription.no_renegotiation);
+ }
+ break;
+ case HandshakeType.client_key_exchange:
+ case HandshakeType.certificate_verify:
+ case HandshakeType.client_hello:
+ default:
+ // We do not support this!
+ this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ break;
+ }
+ }
+
+ private void ProcessApplicationData()
+ {
+ /*
+ * There is nothing we need to do here.
+ *
+ * This function could be used for callbacks when application
+ * data arrives in the future.
+ */
+ }
+
+ private void ProcessAlert()
+ {
+ while (alertQueue.Available >= 2)
+ {
+ /*
+ * An alert is always 2 bytes. Read the alert.
+ */
+ byte[] tmp = new byte[2];
+ alertQueue.Read(tmp, 0, 2, 0);
+ alertQueue.RemoveData(2);
+ byte level = tmp[0];
+ byte description = tmp[1];
+ if (level == (byte)AlertLevel.fatal)
+ {
+ /*
+ * This is a fatal error.
+ */
+ this.failedWithError = true;
+ this.closed = true;
+ /*
+ * Now try to Close the stream, ignore errors.
+ */
+ try
+ {
+ rs.Close();
+ }
+ catch (Exception)
+ {
+ }
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+ else
+ {
+ /*
+ * This is just a warning.
+ */
+ if (description == (byte)AlertDescription.close_notify)
+ {
+ /*
+ * Close notify
+ */
+ this.FailWithError(AlertLevel.warning, AlertDescription.close_notify);
+ }
+ /*
+ * If it is just a warning, we continue.
+ */
+ }
+ }
+ }
+
+ /**
+ * This method is called, when a change cipher spec message is received.
+ *
+ * @throws IOException If the message has an invalid content or the
+ * handshake is not in the correct state.
+ */
+ private void ProcessChangeCipherSpec()
+ {
+ while (changeCipherSpecQueue.Available > 0)
+ {
+ /*
+ * A change cipher spec message is only one byte with the value 1.
+ */
+ byte[] b = new byte[1];
+ changeCipherSpecQueue.Read(b, 0, 1, 0);
+ changeCipherSpecQueue.RemoveData(1);
+ if (b[0] != 1)
+ {
+ /*
+ * This should never happen.
+ */
+ this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ }
+
+ /*
+ * Check if we are in the correct connection state.
+ */
+ if (this.connection_state != CS_CLIENT_FINISHED_SEND)
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ }
+
+ rs.ServerClientSpecReceived();
+
+ this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED;
+ }
+ }
+
+ private void SendClientCertificate(Certificate clientCert)
+ {
+ MemoryStream bos = new MemoryStream();
+ TlsUtilities.WriteUint8((byte)HandshakeType.certificate, bos);
+
+ // Reserve space for length
+ TlsUtilities.WriteUint24(0, bos);
+
+ clientCert.Encode(bos);
+ byte[] message = bos.ToArray();
+
+ // Patch actual length back in
+ TlsUtilities.WriteUint24(message.Length - 4, message, 1);
+
+ rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+ }
+
+ private void SendClientKeyExchange()
+ {
+ MemoryStream bos = new MemoryStream();
+ TlsUtilities.WriteUint8((byte)HandshakeType.client_key_exchange, bos);
+
+ // Reserve space for length
+ TlsUtilities.WriteUint24(0, bos);
+
+ this.keyExchange.GenerateClientKeyExchange(bos);
+ byte[] message = bos.ToArray();
+
+ // Patch actual length back in
+ TlsUtilities.WriteUint24(message.Length - 4, message, 1);
+
+ rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+ }
+
+ private void SendCertificateVerify(byte[] data)
+ {
+ /*
+ * Send signature of handshake messages so far to prove we are the owner of
+ * the cert See RFC 2246 sections 4.7, 7.4.3 and 7.4.8
+ */
+ MemoryStream bos = new MemoryStream();
+ TlsUtilities.WriteUint8((byte)HandshakeType.certificate_verify, bos);
+ TlsUtilities.WriteUint24(data.Length + 2, bos);
+ TlsUtilities.WriteOpaque16(data, bos);
+ byte[] message = bos.ToArray();
+
+ rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+ }
+
+ /// <summary>Connects to the remote system.</summary>
+ /// <param name="verifyer">Will be used when a certificate is received to verify
+ /// that this certificate is accepted by the client.</param>
+ /// <exception cref="IOException">If handshake was not successful</exception>
+ [Obsolete("Use version taking TlsClient")]
+ public virtual void Connect(
+ ICertificateVerifyer verifyer)
+ {
+ this.Connect(new LegacyTlsClient(verifyer));
+ }
+
+ public virtual void Connect(TlsClient tlsClient)
+ {
+ if (tlsClient == null)
+ throw new ArgumentNullException("tlsClient");
+ if (this.tlsClient != null)
+ throw new InvalidOperationException("Connect can only be called once");
+
+ /*
+ * Send Client hello
+ *
+ * First, generate some random data.
+ */
+ this.securityParameters = new SecurityParameters();
+ this.securityParameters.clientRandom = new byte[32];
+ random.NextBytes(securityParameters.clientRandom, 4, 28);
+ TlsUtilities.WriteGmtUnixTime(securityParameters.clientRandom, 0);
+
+ this.tlsClientContext = new TlsClientContextImpl(random, securityParameters);
+ this.tlsClient = tlsClient;
+ this.tlsClient.Init(tlsClientContext);
+
+ MemoryStream outStr = new MemoryStream();
+ TlsUtilities.WriteVersion(outStr);
+ outStr.Write(securityParameters.clientRandom, 0, 32);
+
+ /*
+ * Length of Session id
+ */
+ TlsUtilities.WriteUint8(0, outStr);
+
+ this.offeredCipherSuites = this.tlsClient.GetCipherSuites();
+
+ // ExtensionType -> byte[]
+ this.clientExtensions = this.tlsClient.GetClientExtensions();
+
+ // Cipher Suites (and SCSV)
+ {
+ /*
+ * RFC 5746 3.4.
+ * The client MUST include either an empty "renegotiation_info"
+ * extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling
+ * cipher suite value in the ClientHello. Including both is NOT
+ * RECOMMENDED.
+ */
+ bool noRenegExt = clientExtensions == null
+ || !clientExtensions.Contains(ExtensionType.renegotiation_info);
+
+ int count = offeredCipherSuites.Length;
+ if (noRenegExt)
+ {
+ // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ ++count;
+ }
+
+ TlsUtilities.WriteUint16(2 * count, outStr);
+
+ for (int i = 0; i < offeredCipherSuites.Length; ++i)
+ {
+ TlsUtilities.WriteUint16((int)offeredCipherSuites[i], outStr);
+ }
+
+ if (noRenegExt)
+ {
+ TlsUtilities.WriteUint16((int)CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, outStr);
+ }
+ }
+
+ /*
+ * Compression methods, just the null method.
+ */
+ this.offeredCompressionMethods = tlsClient.GetCompressionMethods();
+
+ {
+ TlsUtilities.WriteUint8((byte)offeredCompressionMethods.Length, outStr);
+ for (int i = 0; i < offeredCompressionMethods.Length; ++i)
+ {
+ TlsUtilities.WriteUint8((byte)offeredCompressionMethods[i], outStr);
+ }
+ }
+
+ // Extensions
+ if (clientExtensions != null)
+ {
+ MemoryStream ext = new MemoryStream();
+
+ foreach (ExtensionType extType in clientExtensions.Keys)
+ {
+ WriteExtension(ext, extType, (byte[])clientExtensions[extType]);
+ }
+
+ TlsUtilities.WriteOpaque16(ext.ToArray(), outStr);
+ }
+
+ MemoryStream bos = new MemoryStream();
+ TlsUtilities.WriteUint8((byte)HandshakeType.client_hello, bos);
+ TlsUtilities.WriteUint24((int)outStr.Length, bos);
+ byte[] outBytes = outStr.ToArray();
+ bos.Write(outBytes, 0, outBytes.Length);
+ byte[] message = bos.ToArray();
+ SafeWriteMessage(ContentType.handshake, message, 0, message.Length);
+ connection_state = CS_CLIENT_HELLO_SEND;
+
+ /*
+ * We will now read data, until we have completed the handshake.
+ */
+ while (connection_state != CS_DONE)
+ {
+ SafeReadData();
+ }
+
+ this.tlsStream = new TlsStream(this);
+ }
+
+ /**
+ * Read data from the network. The method will return immediately, if there is
+ * still some data left in the buffer, or block until some application
+ * data has been read from the network.
+ *
+ * @param buf The buffer where the data will be copied to.
+ * @param offset The position where the data will be placed in the buffer.
+ * @param len The maximum number of bytes to read.
+ * @return The number of bytes read.
+ * @throws IOException If something goes wrong during reading data.
+ */
+ internal int ReadApplicationData(byte[] buf, int offset, int len)
+ {
+ while (applicationDataQueue.Available == 0)
+ {
+ if (this.closed)
+ {
+ /*
+ * We need to read some data.
+ */
+ if (this.failedWithError)
+ {
+ /*
+ * Something went terribly wrong, we should throw an IOException
+ */
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+
+ /*
+ * Connection has been closed, there is no more data to read.
+ */
+ return 0;
+ }
+
+ SafeReadData();
+ }
+ len = System.Math.Min(len, applicationDataQueue.Available);
+ applicationDataQueue.Read(buf, offset, len, 0);
+ applicationDataQueue.RemoveData(len);
+ return len;
+ }
+
+ private void SafeReadData()
+ {
+ try
+ {
+ rs.ReadData();
+ }
+ catch (TlsFatalAlert e)
+ {
+ if (!this.closed)
+ {
+ this.FailWithError(e.AlertDescription, e);
+ }
+ throw e;
+ }
+ catch (IOException e)
+ {
+ if (!this.closed)
+ {
+ this.FailWithError(AlertDescription.internal_error, e);
+ }
+ throw e;
+ }
+ catch (Exception e)
+ {
+ if (!this.closed)
+ {
+ this.FailWithError(AlertDescription.internal_error, e);
+ }
+ throw e;
+ }
+ }
+
+ private void SafeWriteMessage(ContentType type, byte[] buf, int offset, int len)
+ {
+ try
+ {
+ rs.WriteMessage(type, buf, offset, len);
+ }
+ catch (TlsFatalAlert e)
+ {
+ if (!this.closed)
+ {
+ this.FailWithError(e.AlertDescription, e);
+ }
+ throw e;
+ }
+ catch (IOException e)
+ {
+ if (!closed)
+ {
+ this.FailWithError(AlertDescription.internal_error, e);
+ }
+ throw e;
+ }
+ catch (Exception e)
+ {
+ if (!closed)
+ {
+ this.FailWithError(AlertDescription.internal_error, e);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Send some application data to the remote system.
+ * <p/>
+ * The method will handle fragmentation internally.
+ *
+ * @param buf The buffer with the data.
+ * @param offset The position in the buffer where the data is placed.
+ * @param len The length of the data.
+ * @throws IOException If something goes wrong during sending.
+ */
+ internal void WriteData(byte[] buf, int offset, int len)
+ {
+ if (this.closed)
+ {
+ if (this.failedWithError)
+ throw new IOException(TLS_ERROR_MESSAGE);
+
+ throw new IOException("Sorry, connection has been closed, you cannot write more data");
+ }
+
+ /*
+ * Protect against known IV attack!
+ *
+ * DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT
+ * YOU ARE DOING HERE.
+ */
+ SafeWriteMessage(ContentType.application_data, emptybuf, 0, 0);
+
+ do
+ {
+ /*
+ * We are only allowed to write fragments up to 2^14 bytes.
+ */
+ int toWrite = System.Math.Min(len, 1 << 14);
+
+ SafeWriteMessage(ContentType.application_data, buf, offset, toWrite);
+
+ offset += toWrite;
+ len -= toWrite;
+ }
+ while (len > 0);
+ }
+
+ /// <summary>A Stream which can be used to send data.</summary>
+ [Obsolete("Use 'Stream' property instead")]
+ public virtual Stream OutputStream
+ {
+ get { return this.tlsStream; }
+ }
+
+ /// <summary>A Stream which can be used to read data.</summary>
+ [Obsolete("Use 'Stream' property instead")]
+ public virtual Stream InputStream
+ {
+ get { return this.tlsStream; }
+ }
+
+ /// <summary>The secure bidirectional stream for this connection</summary>
+ public virtual Stream Stream
+ {
+ get { return this.tlsStream; }
+ }
+
+ /**
+ * Terminate this connection with an alert.
+ * <p/>
+ * Can be used for normal closure too.
+ *
+ * @param alertLevel The level of the alert, an be AlertLevel.fatal or AL_warning.
+ * @param alertDescription The exact alert message.
+ * @throws IOException If alert was fatal.
+ */
+ private void FailWithError(AlertLevel alertLevel, AlertDescription alertDescription)
+ {
+ this.FailWithError(alertLevel, alertDescription, null);
+ }
+
+ private void FailWithError(AlertDescription alertDescription, Exception ex)
+ {
+ this.FailWithError(AlertLevel.fatal, alertDescription, ex);
+ }
+
+ private void FailWithError(AlertLevel alertLevel, AlertDescription alertDescription, Exception ex)
+ {
+ /*
+ * Check if the connection is still open.
+ */
+ if (!closed)
+ {
+ /*
+ * Prepare the message
+ */
+ this.closed = true;
+
+ if (alertLevel == AlertLevel.fatal)
+ {
+ /*
+ * This is a fatal message.
+ */
+ this.failedWithError = true;
+ }
+ SendAlert(alertLevel, alertDescription);
+ rs.Close();
+ if (alertLevel == AlertLevel.fatal)
+ {
+ throw new IOException(TLS_ERROR_MESSAGE, ex);
+ }
+ }
+ else
+ {
+ throw new IOException(TLS_ERROR_MESSAGE, ex);
+ }
+ }
+
+ internal void SendAlert(AlertLevel alertLevel, AlertDescription alertDescription)
+ {
+ byte[] error = new byte[2];
+ error[0] = (byte)alertLevel;
+ error[1] = (byte)alertDescription;
+
+ rs.WriteMessage(ContentType.alert, error, 0, 2);
+ }
+
+ /// <summary>Closes this connection</summary>
+ /// <exception cref="IOException">If something goes wrong during closing.</exception>
+ public virtual void Close()
+ {
+ if (!closed)
+ {
+ this.FailWithError(AlertLevel.warning, AlertDescription.close_notify);
+ }
+ }
+
+ /**
+ * Make sure the Stream is now empty. Fail otherwise.
+ *
+ * @param is The Stream to check.
+ * @throws IOException If is is not empty.
+ */
+ internal void AssertEmpty(
+ MemoryStream inStr)
+ {
+ if (inStr.Position < inStr.Length)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ }
+
+ internal void Flush()
+ {
+ rs.Flush();
+ }
+
+ internal bool IsClosed
+ {
+ get { return closed; }
+ }
+
+ private static bool ArrayContains(CipherSuite[] a, CipherSuite n)
+ {
+ for (int i = 0; i < a.Length; ++i)
+ {
+ if (a[i] == n)
+ return true;
+ }
+ return false;
+ }
+
+ private static bool ArrayContains(CompressionMethod[] a, CompressionMethod n)
+ {
+ for (int i = 0; i < a.Length; ++i)
+ {
+ if (a[i] == n)
+ return true;
+ }
+ return false;
+ }
+
+ private static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection)
+ {
+ MemoryStream buf = new MemoryStream();
+ TlsUtilities.WriteOpaque8(renegotiated_connection, buf);
+ return buf.ToArray();
+ }
+
+ private static void WriteExtension(Stream output, ExtensionType extType, byte[] extValue)
+ {
+ TlsUtilities.WriteUint16((int)extType, output);
+ TlsUtilities.WriteOpaque16(extValue, output);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsPskIdentity.cs b/Crypto/src/crypto/tls/TlsPskIdentity.cs
new file mode 100644
index 000000000..119064ee7
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsPskIdentity.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsPskIdentity
+ {
+ void SkipIdentityHint();
+
+ void NotifyIdentityHint(byte[] psk_identity_hint);
+
+ byte[] GetPskIdentity();
+
+ byte[] GetPsk();
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsPskKeyExchange.cs b/Crypto/src/crypto/tls/TlsPskKeyExchange.cs
new file mode 100644
index 000000000..226153a97
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -0,0 +1,149 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsPskKeyExchange
+ : TlsKeyExchange
+ {
+ protected TlsClientContext context;
+ protected KeyExchangeAlgorithm keyExchange;
+ protected TlsPskIdentity pskIdentity;
+
+ protected byte[] psk_identity_hint = null;
+
+ protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
+ protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+
+ protected RsaKeyParameters rsaServerPublicKey = null;
+ protected byte[] premasterSecret;
+
+ internal TlsPskKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange,
+ TlsPskIdentity pskIdentity)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.PSK:
+ case KeyExchangeAlgorithm.RSA_PSK:
+ case KeyExchangeAlgorithm.DHE_PSK:
+ break;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+
+ this.context = context;
+ this.keyExchange = keyExchange;
+ this.pskIdentity = pskIdentity;
+ }
+
+ public virtual void SkipServerCertificate()
+ {
+ // OK
+ }
+
+ public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void SkipServerKeyExchange()
+ {
+ this.psk_identity_hint = new byte[0];
+ }
+
+ public virtual void ProcessServerKeyExchange(Stream input)
+ {
+ this.psk_identity_hint = TlsUtilities.ReadOpaque16(input);
+
+ if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ byte[] pBytes = TlsUtilities.ReadOpaque16(input);
+ byte[] gBytes = TlsUtilities.ReadOpaque16(input);
+ byte[] YsBytes = TlsUtilities.ReadOpaque16(input);
+
+ BigInteger p = new BigInteger(1, pBytes);
+ BigInteger g = new BigInteger(1, gBytes);
+ BigInteger Ys = new BigInteger(1, YsBytes);
+
+ this.dhAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey(
+ new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+ }
+ else if (this.psk_identity_hint.Length == 0)
+ {
+ // TODO Should we enforce that this message should have been skipped if hint is empty?
+ //throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void SkipClientCredentials()
+ {
+ // OK
+ }
+
+ public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual void GenerateClientKeyExchange(Stream output)
+ {
+ if (psk_identity_hint == null || psk_identity_hint.Length == 0)
+ {
+ pskIdentity.SkipIdentityHint();
+ }
+ else
+ {
+ pskIdentity.NotifyIdentityHint(psk_identity_hint);
+ }
+
+ byte[] psk_identity = pskIdentity.GetPskIdentity();
+
+ TlsUtilities.WriteOpaque16(psk_identity, output);
+
+ if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ {
+ this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(
+ context.SecureRandom, this.rsaServerPublicKey, output);
+ }
+ else if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(
+ context.SecureRandom, this.dhAgreeServerPublicKey.Parameters, output);
+ }
+ }
+
+ public virtual byte[] GeneratePremasterSecret()
+ {
+ byte[] psk = pskIdentity.GetPsk();
+ byte[] other_secret = GenerateOtherSecret(psk.Length);
+
+ MemoryStream buf = new MemoryStream(4 + other_secret.Length + psk.Length);
+ TlsUtilities.WriteOpaque16(other_secret, buf);
+ TlsUtilities.WriteOpaque16(psk, buf);
+ return buf.ToArray();
+ }
+
+ protected virtual byte[] GenerateOtherSecret(int pskLength)
+ {
+ if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ return TlsDHUtilities.CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+ }
+
+ if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ {
+ return this.premasterSecret;
+ }
+
+ return new byte[pskLength];
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/Crypto/src/crypto/tls/TlsRsaKeyExchange.cs
new file mode 100644
index 000000000..4538a2a81
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -0,0 +1,165 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// TLS 1.0 RSA key exchange.
+ /// </summary>
+ internal class TlsRsaKeyExchange
+ : TlsKeyExchange
+ {
+ protected TlsClientContext context;
+
+ protected AsymmetricKeyParameter serverPublicKey = null;
+
+ protected RsaKeyParameters rsaServerPublicKey = null;
+
+ protected byte[] premasterSecret;
+
+ internal TlsRsaKeyExchange(TlsClientContext context)
+ {
+ this.context = context;
+ }
+
+ public virtual void SkipServerCertificate()
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ X509CertificateStructure x509Cert = serverCertificate.certs[0];
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+
+ try
+ {
+ this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+ }
+// catch (RuntimeException)
+ catch (Exception)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ // Sanity check the PublicKeyFactory
+ if (this.serverPublicKey.IsPrivate)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.rsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.serverPublicKey);
+
+ TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment);
+
+ // TODO
+ /*
+ * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
+ * signing algorithm for the certificate must be the same as the algorithm for the
+ * certificate key."
+ */
+ }
+
+ public virtual void SkipServerKeyExchange()
+ {
+ // OK
+ }
+
+ public virtual void ProcessServerKeyExchange(Stream input)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ {
+ ClientCertificateType[] types = certificateRequest.CertificateTypes;
+ foreach (ClientCertificateType type in types)
+ {
+ switch (type)
+ {
+ case ClientCertificateType.rsa_sign:
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+ }
+
+ public virtual void SkipClientCredentials()
+ {
+ // OK
+ }
+
+ public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ if (!(clientCredentials is TlsSignerCredentials))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual void GenerateClientKeyExchange(Stream output)
+ {
+ this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(
+ context.SecureRandom, this.rsaServerPublicKey, output);
+ }
+
+ public virtual byte[] GeneratePremasterSecret()
+ {
+ byte[] tmp = this.premasterSecret;
+ this.premasterSecret = null;
+ return tmp;
+ }
+
+ // Would be needed to process RSA_EXPORT server key exchange
+// protected virtual void ProcessRsaServerKeyExchange(Stream input, ISigner signer)
+// {
+// Stream sigIn = input;
+// if (signer != null)
+// {
+// sigIn = new SignerStream(input, signer, null);
+// }
+//
+// byte[] modulusBytes = TlsUtilities.ReadOpaque16(sigIn);
+// byte[] exponentBytes = TlsUtilities.ReadOpaque16(sigIn);
+//
+// if (signer != null)
+// {
+// byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+//
+// if (!signer.VerifySignature(sigByte))
+// {
+// handler.FailWithError(AlertLevel.fatal, AlertDescription.bad_certificate);
+// }
+// }
+//
+// BigInteger modulus = new BigInteger(1, modulusBytes);
+// BigInteger exponent = new BigInteger(1, exponentBytes);
+//
+// this.rsaServerPublicKey = ValidateRSAPublicKey(new RsaKeyParameters(false, modulus, exponent));
+// }
+
+ protected virtual RsaKeyParameters ValidateRsaPublicKey(RsaKeyParameters key)
+ {
+ // TODO What is the minimum bit length required?
+// key.Modulus.BitLength;
+
+ if (!key.Exponent.IsProbablePrime(2))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return key;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsRsaSigner.cs b/Crypto/src/crypto/tls/TlsRsaSigner.cs
new file mode 100644
index 000000000..a50ff9558
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsRsaSigner.cs
@@ -0,0 +1,53 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsRsaSigner
+ : TlsSigner
+ {
+ public virtual byte[] CalculateRawSignature(SecureRandom random,
+ AsymmetricKeyParameter privateKey, byte[] md5andsha1)
+ {
+ ISigner s = MakeSigner(new NullDigest(), true, new ParametersWithRandom(privateKey, random));
+ s.BlockUpdate(md5andsha1, 0, md5andsha1.Length);
+ return s.GenerateSignature();
+ }
+
+ public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey,
+ byte[] md5andsha1)
+ {
+ ISigner s = MakeSigner(new NullDigest(), false, publicKey);
+ s.BlockUpdate(md5andsha1, 0, md5andsha1.Length);
+ return s.VerifySignature(sigBytes);
+ }
+
+ public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey)
+ {
+ return MakeSigner(new CombinedHash(), true, new ParametersWithRandom(privateKey, random));
+ }
+
+ public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey)
+ {
+ return MakeSigner(new CombinedHash(), false, publicKey);
+ }
+
+ public virtual bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+ {
+ return publicKey is RsaKeyParameters && !publicKey.IsPrivate;
+ }
+
+ protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp)
+ {
+ ISigner s = new GenericSigner(new Pkcs1Encoding(new RsaBlindedEngine()), d);
+ s.Init(forSigning, cp);
+ return s;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsRsaUtilities.cs b/Crypto/src/crypto/tls/TlsRsaUtilities.cs
new file mode 100644
index 000000000..4450ba452
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsRsaUtilities.cs
@@ -0,0 +1,42 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class TlsRsaUtilities
+ {
+ public static byte[] GenerateEncryptedPreMasterSecret(SecureRandom random,
+ RsaKeyParameters rsaServerPublicKey, Stream output)
+ {
+ /*
+ * Choose a PremasterSecret and send it encrypted to the server
+ */
+ byte[] premasterSecret = new byte[48];
+ random.NextBytes(premasterSecret);
+ TlsUtilities.WriteVersion(premasterSecret, 0);
+
+ Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine());
+ encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, random));
+
+ try
+ {
+ byte[] keData = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length);
+ TlsUtilities.WriteOpaque16(keData, output);
+ }
+ catch (InvalidCipherTextException)
+ {
+ /*
+ * This should never happen, only during decryption.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return premasterSecret;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsSigner.cs b/Crypto/src/crypto/tls/TlsSigner.cs
new file mode 100644
index 000000000..e59b90705
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsSigner.cs
@@ -0,0 +1,18 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsSigner
+ {
+ byte[] CalculateRawSignature(SecureRandom random, AsymmetricKeyParameter privateKey,
+ byte[] md5andsha1);
+ bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1);
+
+ ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey);
+ ISigner CreateVerifyer(AsymmetricKeyParameter publicKey);
+
+ bool IsValidPublicKey(AsymmetricKeyParameter publicKey);
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsSignerCredentials.cs b/Crypto/src/crypto/tls/TlsSignerCredentials.cs
new file mode 100644
index 000000000..2adb06c26
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsSignerCredentials.cs
@@ -0,0 +1,11 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsSignerCredentials : TlsCredentials
+ {
+ /// <exception cref="IOException"></exception>
+ byte[] GenerateCertificateSignature(byte[] md5andsha1);
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/Crypto/src/crypto/tls/TlsSrpKeyExchange.cs
new file mode 100644
index 000000000..852aace41
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsSrpKeyExchange.cs
@@ -0,0 +1,203 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <summary>
+ /// TLS 1.1 SRP key exchange.
+ /// </summary>
+ internal class TlsSrpKeyExchange
+ : TlsKeyExchange
+ {
+ protected TlsClientContext context;
+ protected KeyExchangeAlgorithm keyExchange;
+ protected TlsSigner tlsSigner;
+ protected byte[] identity;
+ protected byte[] password;
+
+ protected AsymmetricKeyParameter serverPublicKey = null;
+
+ protected byte[] s = null;
+ protected BigInteger B = null;
+ protected Srp6Client srpClient = new Srp6Client();
+
+ internal TlsSrpKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange,
+ byte[] identity, byte[] password)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.SRP:
+ this.tlsSigner = null;
+ break;
+ case KeyExchangeAlgorithm.SRP_RSA:
+ this.tlsSigner = new TlsRsaSigner();
+ break;
+ case KeyExchangeAlgorithm.SRP_DSS:
+ this.tlsSigner = new TlsDssSigner();
+ break;
+ default:
+ throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+ }
+
+ this.context = context;
+ this.keyExchange = keyExchange;
+ this.identity = identity;
+ this.password = password;
+ }
+
+ public virtual void SkipServerCertificate()
+ {
+ if (tlsSigner != null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ public virtual void ProcessServerCertificate(Certificate serverCertificate)
+ {
+ if (tlsSigner == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ X509CertificateStructure x509Cert = serverCertificate.certs[0];
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+
+ try
+ {
+ this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+ }
+// catch (RuntimeException)
+ catch (Exception)
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+
+ // TODO
+ /*
+ * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
+ * signing algorithm for the certificate must be the same as the algorithm for the
+ * certificate key."
+ */
+ }
+
+ public virtual void SkipServerKeyExchange()
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void ProcessServerKeyExchange(Stream input)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+
+ Stream sigIn = input;
+ ISigner signer = null;
+
+ if (tlsSigner != null)
+ {
+ signer = InitSigner(tlsSigner, securityParameters);
+ sigIn = new SignerStream(input, signer, null);
+ }
+
+ byte[] NBytes = TlsUtilities.ReadOpaque16(sigIn);
+ byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn);
+ byte[] sBytes = TlsUtilities.ReadOpaque8(sigIn);
+ byte[] BBytes = TlsUtilities.ReadOpaque16(sigIn);
+
+ if (signer != null)
+ {
+ byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+
+ if (!signer.VerifySignature(sigByte))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+ }
+
+ BigInteger N = new BigInteger(1, NBytes);
+ BigInteger g = new BigInteger(1, gBytes);
+
+ // TODO Validate group parameters (see RFC 5054)
+ //throw new TlsFatalAlert(AlertDescription.insufficient_security);
+
+ this.s = sBytes;
+
+ /*
+ * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter"
+ * alert if B % N = 0.
+ */
+ try
+ {
+ this.B = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes));
+ }
+ catch (CryptoException)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ this.srpClient.Init(N, g, new Sha1Digest(), context.SecureRandom);
+ }
+
+ public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public virtual void SkipClientCredentials()
+ {
+ // OK
+ }
+
+ public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ public virtual void GenerateClientKeyExchange(Stream output)
+ {
+ byte[] keData = BigIntegers.AsUnsignedByteArray(srpClient.GenerateClientCredentials(s,
+ this.identity, this.password));
+ TlsUtilities.WriteOpaque16(keData, output);
+ }
+
+ public virtual byte[] GeneratePremasterSecret()
+ {
+ try
+ {
+ // TODO Check if this needs to be a fixed size
+ return BigIntegers.AsUnsignedByteArray(srpClient.CalculateSecret(B));
+ }
+ catch (CryptoException)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ {
+ ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+ signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+ signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+ return signer;
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsStream.cs b/Crypto/src/crypto/tls/TlsStream.cs
new file mode 100644
index 000000000..e3d05686b
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsStream.cs
@@ -0,0 +1,89 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsStream
+ : Stream
+ {
+ private readonly TlsProtocolHandler handler;
+
+ internal TlsStream(
+ TlsProtocolHandler handler)
+ {
+ this.handler = handler;
+ }
+
+ public override bool CanRead
+ {
+ get { return !handler.IsClosed; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return !handler.IsClosed; }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ handler.Close();
+ }
+ }
+
+ public override void Flush()
+ {
+ handler.Flush();
+ }
+
+ public override long Length
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ return this.handler.ReadApplicationData(buf, off, len);
+ }
+
+ public override int ReadByte()
+ {
+ byte[] buf = new byte[1];
+ if (this.Read(buf, 0, 1) <= 0)
+ return -1;
+ return buf[0];
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ this.handler.WriteData(buf, off, len);
+ }
+
+ public override void WriteByte(byte b)
+ {
+ this.handler.WriteData(new byte[] { b }, 0, 1);
+ }
+ }
+}
diff --git a/Crypto/src/crypto/tls/TlsUtilities.cs b/Crypto/src/crypto/tls/TlsUtilities.cs
new file mode 100644
index 000000000..0e2452689
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsUtilities.cs
@@ -0,0 +1,286 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ /// <remarks>Some helper fuctions for MicroTLS.</remarks>
+ public class TlsUtilities
+ {
+ internal static void WriteUint8(byte i, Stream os)
+ {
+ os.WriteByte(i);
+ }
+
+ internal static void WriteUint8(byte i, byte[] buf, int offset)
+ {
+ buf[offset] = i;
+ }
+
+ internal static void WriteUint16(int i, Stream os)
+ {
+ os.WriteByte((byte)(i >> 8));
+ os.WriteByte((byte)i);
+ }
+
+ internal static void WriteUint16(int i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)(i >> 8);
+ buf[offset + 1] = (byte)i;
+ }
+
+ internal static void WriteUint24(int i, Stream os)
+ {
+ os.WriteByte((byte)(i >> 16));
+ os.WriteByte((byte)(i >> 8));
+ os.WriteByte((byte)i);
+ }
+
+ internal static void WriteUint24(int i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)(i >> 16);
+ buf[offset + 1] = (byte)(i >> 8);
+ buf[offset + 2] = (byte)(i);
+ }
+
+ internal static void WriteUint64(long i, Stream os)
+ {
+ os.WriteByte((byte)(i >> 56));
+ os.WriteByte((byte)(i >> 48));
+ os.WriteByte((byte)(i >> 40));
+ os.WriteByte((byte)(i >> 32));
+ os.WriteByte((byte)(i >> 24));
+ os.WriteByte((byte)(i >> 16));
+ os.WriteByte((byte)(i >> 8));
+ os.WriteByte((byte)i);
+ }
+
+ internal static void WriteUint64(long i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)(i >> 56);
+ buf[offset + 1] = (byte)(i >> 48);
+ buf[offset + 2] = (byte)(i >> 40);
+ buf[offset + 3] = (byte)(i >> 32);
+ buf[offset + 4] = (byte)(i >> 24);
+ buf[offset + 5] = (byte)(i >> 16);
+ buf[offset + 6] = (byte)(i >> 8);
+ buf[offset + 7] = (byte)(i);
+ }
+
+ internal static void WriteOpaque8(byte[] buf, Stream os)
+ {
+ WriteUint8((byte)buf.Length, os);
+ os.Write(buf, 0, buf.Length);
+ }
+
+ internal static void WriteOpaque16(byte[] buf, Stream os)
+ {
+ WriteUint16(buf.Length, os);
+ os.Write(buf, 0, buf.Length);
+ }
+
+ internal static void WriteOpaque24(byte[] buf, Stream os)
+ {
+ WriteUint24(buf.Length, os);
+ os.Write(buf, 0, buf.Length);
+ }
+
+ internal static void WriteUint8Array(byte[] uints, Stream os)
+ {
+ os.Write(uints, 0, uints.Length);
+ }
+
+ internal static void WriteUint16Array(int[] uints, Stream os)
+ {
+ for (int i = 0; i < uints.Length; ++i)
+ {
+ WriteUint16(uints[i], os);
+ }
+ }
+
+ internal static byte ReadUint8(Stream inStr)
+ {
+ int i = inStr.ReadByte();
+ if (i < 0)
+ {
+ throw new EndOfStreamException();
+ }
+ return (byte)i;
+ }
+
+ internal static int ReadUint16(Stream inStr)
+ {
+ int i1 = inStr.ReadByte();
+ int i2 = inStr.ReadByte();
+ if ((i1 | i2) < 0)
+ {
+ throw new EndOfStreamException();
+ }
+ return i1 << 8 | i2;
+ }
+
+ internal static int ReadUint24(Stream inStr)
+ {
+ int i1 = inStr.ReadByte();
+ int i2 = inStr.ReadByte();
+ int i3 = inStr.ReadByte();
+ if ((i1 | i2 | i3) < 0)
+ {
+ throw new EndOfStreamException();
+ }
+ return (i1 << 16) | (i2 << 8) | i3;
+ }
+
+ internal static void ReadFully(byte[] buf, Stream inStr)
+ {
+ if (Streams.ReadFully(inStr, buf, 0, buf.Length) < buf.Length)
+ throw new EndOfStreamException();
+ }
+
+ internal static byte[] ReadOpaque8(Stream inStr)
+ {
+ byte length = ReadUint8(inStr);
+ byte[] bytes = new byte[length];
+ ReadFully(bytes, inStr);
+ return bytes;
+ }
+
+ internal static byte[] ReadOpaque16(Stream inStr)
+ {
+ int length = ReadUint16(inStr);
+ byte[] bytes = new byte[length];
+ ReadFully(bytes, inStr);
+ return bytes;
+ }
+
+ internal static void CheckVersion(byte[] readVersion)
+ {
+ if ((readVersion[0] != 3) || (readVersion[1] != 1))
+ {
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+ }
+ }
+
+ internal static void CheckVersion(Stream inStr)
+ {
+ int i1 = inStr.ReadByte();
+ int i2 = inStr.ReadByte();
+ if ((i1 != 3) || (i2 != 1))
+ {
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+ }
+ }
+
+ internal static void WriteGmtUnixTime(byte[] buf, int offset)
+ {
+ int t = (int)(DateTimeUtilities.CurrentUnixMs() / 1000L);
+ buf[offset] = (byte)(t >> 24);
+ buf[offset + 1] = (byte)(t >> 16);
+ buf[offset + 2] = (byte)(t >> 8);
+ buf[offset + 3] = (byte)t;
+ }
+
+ internal static void WriteVersion(Stream os)
+ {
+ os.WriteByte(3);
+ os.WriteByte(1);
+ }
+
+ internal static void WriteVersion(byte[] buf, int offset)
+ {
+ buf[offset] = 3;
+ buf[offset + 1] = 1;
+ }
+
+ private static void hmac_hash(IDigest digest, byte[] secret, byte[] seed, byte[] output)
+ {
+ HMac mac = new HMac(digest);
+ KeyParameter param = new KeyParameter(secret);
+ byte[] a = seed;
+ int size = digest.GetDigestSize();
+ int iterations = (output.Length + size - 1) / size;
+ byte[] buf = new byte[mac.GetMacSize()];
+ byte[] buf2 = new byte[mac.GetMacSize()];
+ for (int i = 0; i < iterations; i++)
+ {
+ mac.Init(param);
+ mac.BlockUpdate(a, 0, a.Length);
+ mac.DoFinal(buf, 0);
+ a = buf;
+ mac.Init(param);
+ mac.BlockUpdate(a, 0, a.Length);
+ mac.BlockUpdate(seed, 0, seed.Length);
+ mac.DoFinal(buf2, 0);
+ Array.Copy(buf2, 0, output, (size * i), System.Math.Min(size, output.Length - (size * i)));
+ }
+ }
+
+ internal static byte[] PRF(byte[] secret, string asciiLabel, byte[] seed, int size)
+ {
+ byte[] label = Strings.ToAsciiByteArray(asciiLabel);
+
+ int s_half = (secret.Length + 1) / 2;
+ byte[] s1 = new byte[s_half];
+ byte[] s2 = new byte[s_half];
+ Array.Copy(secret, 0, s1, 0, s_half);
+ Array.Copy(secret, secret.Length - s_half, s2, 0, s_half);
+
+ byte[] ls = Concat(label, seed);
+
+ byte[] buf = new byte[size];
+ byte[] prf = new byte[size];
+ hmac_hash(new MD5Digest(), s1, ls, prf);
+ hmac_hash(new Sha1Digest(), s2, ls, buf);
+ for (int i = 0; i < size; i++)
+ {
+ buf[i] ^= prf[i];
+ }
+ return buf;
+ }
+
+ internal static byte[] PRF_1_2(IDigest digest, byte[] secret, string asciiLabel, byte[] seed, int size)
+ {
+ byte[] label = Strings.ToAsciiByteArray(asciiLabel);
+ byte[] labelSeed = Concat(label, seed);
+
+ byte[] buf = new byte[size];
+ hmac_hash(digest, secret, labelSeed, buf);
+ return buf;
+ }
+
+ internal static byte[] Concat(byte[] a, byte[] b)
+ {
+ byte[] c = new byte[a.Length + b.Length];
+ Array.Copy(a, 0, c, 0, a.Length);
+ Array.Copy(b, 0, c, a.Length, b.Length);
+ return c;
+ }
+
+ internal static void ValidateKeyUsage(X509CertificateStructure c, int keyUsageBits)
+ {
+ X509Extensions exts = c.TbsCertificate.Extensions;
+ if (exts != null)
+ {
+ X509Extension ext = exts.GetExtension(X509Extensions.KeyUsage);
+ if (ext != null)
+ {
+ DerBitString ku = KeyUsage.GetInstance(ext);
+ //int bits = ku.GetBytes()[0];
+ //if ((bits & keyUsageBits) != keyUsageBits)
+ //{
+ // throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ //}
+ }
+ }
+ }
+ }
+}
diff --git a/Crypto/src/crypto/util/Pack.cs b/Crypto/src/crypto/util/Pack.cs
new file mode 100644
index 000000000..67c939ad5
--- /dev/null
+++ b/Crypto/src/crypto/util/Pack.cs
@@ -0,0 +1,219 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Utilities
+{
+ internal sealed class Pack
+ {
+ private Pack()
+ {
+ }
+
+ internal static void UInt16_To_BE(ushort n, byte[] bs)
+ {
+ bs[0] = (byte)(n >> 8);
+ bs[1] = (byte)(n );
+ }
+
+ internal static void UInt16_To_BE(ushort n, byte[] bs, int off)
+ {
+ bs[ off] = (byte)(n >> 8);
+ bs[++off] = (byte)(n );
+ }
+
+ internal static ushort BE_To_UInt16(byte[] bs)
+ {
+ uint n = (uint)bs[0] << 8;
+ n |= (uint)bs[1];
+ return (ushort)n;
+ }
+
+ internal static ushort BE_To_UInt16(byte[] bs, int off)
+ {
+ uint n = (uint)bs[off] << 8;
+ n |= (uint)bs[++off];
+ return (ushort)n;
+ }
+
+ internal static void UInt32_To_BE(uint n, byte[] bs)
+ {
+ bs[0] = (byte)(n >> 24);
+ bs[1] = (byte)(n >> 16);
+ bs[2] = (byte)(n >> 8);
+ bs[3] = (byte)(n );
+ }
+
+ internal static void UInt32_To_BE(uint n, byte[] bs, int off)
+ {
+ bs[ off] = (byte)(n >> 24);
+ bs[++off] = (byte)(n >> 16);
+ bs[++off] = (byte)(n >> 8);
+ bs[++off] = (byte)(n );
+ }
+
+ internal static void UInt32_To_BE(uint[] ns, byte[] bs, int off)
+ {
+ for (int i = 0; i < ns.Length; ++i)
+ {
+ UInt32_To_BE(ns[i], bs, off);
+ off += 4;
+ }
+ }
+
+ internal static uint BE_To_UInt32(byte[] bs)
+ {
+ uint n = (uint)bs[0] << 24;
+ n |= (uint)bs[1] << 16;
+ n |= (uint)bs[2] << 8;
+ n |= (uint)bs[3];
+ return n;
+ }
+
+ internal static uint BE_To_UInt32(byte[] bs, int off)
+ {
+ uint n = (uint)bs[off] << 24;
+ n |= (uint)bs[++off] << 16;
+ n |= (uint)bs[++off] << 8;
+ n |= (uint)bs[++off];
+ return n;
+ }
+
+ internal static void BE_To_UInt32(byte[] bs, int off, uint[] ns)
+ {
+ for (int i = 0; i < ns.Length; ++i)
+ {
+ ns[i] = BE_To_UInt32(bs, off);
+ off += 4;
+ }
+ }
+
+ internal static ulong BE_To_UInt64(byte[] bs)
+ {
+ uint hi = BE_To_UInt32(bs);
+ uint lo = BE_To_UInt32(bs, 4);
+ return ((ulong)hi << 32) | (ulong)lo;
+ }
+
+ internal static ulong BE_To_UInt64(byte[] bs, int off)
+ {
+ uint hi = BE_To_UInt32(bs, off);
+ uint lo = BE_To_UInt32(bs, off + 4);
+ return ((ulong)hi << 32) | (ulong)lo;
+ }
+
+ internal static void UInt64_To_BE(ulong n, byte[] bs)
+ {
+ UInt32_To_BE((uint)(n >> 32), bs);
+ UInt32_To_BE((uint)(n ), bs, 4);
+ }
+
+ internal static void UInt64_To_BE(ulong n, byte[] bs, int off)
+ {
+ UInt32_To_BE((uint)(n >> 32), bs, off);
+ UInt32_To_BE((uint)(n ), bs, off + 4);
+ }
+
+ internal static void UInt16_To_LE(ushort n, byte[] bs)
+ {
+ bs[0] = (byte)(n );
+ bs[1] = (byte)(n >> 8);
+ }
+
+ internal static void UInt16_To_LE(ushort n, byte[] bs, int off)
+ {
+ bs[ off] = (byte)(n );
+ bs[++off] = (byte)(n >> 8);
+ }
+
+ internal static ushort LE_To_UInt16(byte[] bs)
+ {
+ uint n = (uint)bs[0];
+ n |= (uint)bs[1] << 8;
+ return (ushort)n;
+ }
+
+ internal static ushort LE_To_UInt16(byte[] bs, int off)
+ {
+ uint n = (uint)bs[off];
+ n |= (uint)bs[++off] << 8;
+ return (ushort)n;
+ }
+
+ internal static void UInt32_To_LE(uint n, byte[] bs)
+ {
+ bs[0] = (byte)(n );
+ bs[1] = (byte)(n >> 8);
+ bs[2] = (byte)(n >> 16);
+ bs[3] = (byte)(n >> 24);
+ }
+
+ internal static void UInt32_To_LE(uint n, byte[] bs, int off)
+ {
+ bs[ off] = (byte)(n );
+ bs[++off] = (byte)(n >> 8);
+ bs[++off] = (byte)(n >> 16);
+ bs[++off] = (byte)(n >> 24);
+ }
+
+ internal static void UInt32_To_LE(uint[] ns, byte[] bs, int off)
+ {
+ for (int i = 0; i < ns.Length; ++i)
+ {
+ UInt32_To_LE(ns[i], bs, off);
+ off += 4;
+ }
+ }
+
+ internal static uint LE_To_UInt32(byte[] bs)
+ {
+ uint n = (uint)bs[0];
+ n |= (uint)bs[1] << 8;
+ n |= (uint)bs[2] << 16;
+ n |= (uint)bs[3] << 24;
+ return n;
+ }
+
+ internal static uint LE_To_UInt32(byte[] bs, int off)
+ {
+ uint n = (uint)bs[off];
+ n |= (uint)bs[++off] << 8;
+ n |= (uint)bs[++off] << 16;
+ n |= (uint)bs[++off] << 24;
+ return n;
+ }
+
+ internal static void LE_To_UInt32(byte[] bs, int off, uint[] ns)
+ {
+ for (int i = 0; i < ns.Length; ++i)
+ {
+ ns[i] = LE_To_UInt32(bs, off);
+ off += 4;
+ }
+ }
+
+ internal static ulong LE_To_UInt64(byte[] bs)
+ {
+ uint lo = LE_To_UInt32(bs);
+ uint hi = LE_To_UInt32(bs, 4);
+ return ((ulong)hi << 32) | (ulong)lo;
+ }
+
+ internal static ulong LE_To_UInt64(byte[] bs, int off)
+ {
+ uint lo = LE_To_UInt32(bs, off);
+ uint hi = LE_To_UInt32(bs, off + 4);
+ return ((ulong)hi << 32) | (ulong)lo;
+ }
+
+ internal static void UInt64_To_LE(ulong n, byte[] bs)
+ {
+ UInt32_To_LE((uint)(n ), bs);
+ UInt32_To_LE((uint)(n >> 32), bs, 4);
+ }
+
+ internal static void UInt64_To_LE(ulong n, byte[] bs, int off)
+ {
+ UInt32_To_LE((uint)(n ), bs, off);
+ UInt32_To_LE((uint)(n >> 32), bs, off + 4);
+ }
+ }
+}
|