diff --git a/src/core/srcbc/crypto/AsymmetricCipherKeyPair.cs b/src/core/srcbc/crypto/AsymmetricCipherKeyPair.cs
new file mode 100644
index 0000000..dca2522
--- /dev/null
+++ b/src/core/srcbc/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/src/core/srcbc/crypto/AsymmetricKeyParameter.cs b/src/core/srcbc/crypto/AsymmetricKeyParameter.cs
new file mode 100644
index 0000000..335b587
--- /dev/null
+++ b/src/core/srcbc/crypto/AsymmetricKeyParameter.cs
@@ -0,0 +1,47 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Crypto
+{
+ public class AsymmetricKeyParameter
+ : ICipherParameters
+ {
+ private readonly bool privateKey;
+
+ public 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/src/core/srcbc/crypto/BufferedAeadBlockCipher.cs b/src/core/srcbc/crypto/BufferedAeadBlockCipher.cs
new file mode 100644
index 0000000..bd11f15
--- /dev/null
+++ b/src/core/srcbc/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/src/core/srcbc/crypto/BufferedAsymmetricBlockCipher.cs b/src/core/srcbc/crypto/BufferedAsymmetricBlockCipher.cs
new file mode 100644
index 0000000..0e5bc3e
--- /dev/null
+++ b/src/core/srcbc/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();
+ }
+
+ ///
+ * 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. + *
+ */ + 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) + { + int total = length + bufOff; + int leftOver = total % buf.Length; + if (leftOver == 0) + { + return total; + } + return total - leftOver + buf.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) + { + 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; + } + } + + 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) + { + 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); + } + + int resultLen = bufOff; + + Reset(); + + return resultLen; + } + + /** + * 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/src/core/srcbc/crypto/BufferedCipherBase.cs b/src/core/srcbc/crypto/BufferedCipherBase.cs new file mode 100644 index 0000000..f87f38c --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/BufferedIesCipher.cs b/src/core/srcbc/crypto/BufferedIesCipher.cs new file mode 100644 index 0000000..95555c0 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/BufferedStreamCipher.cs b/src/core/srcbc/crypto/BufferedStreamCipher.cs new file mode 100644 index 0000000..0c565b7 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/CipherKeyGenerator.cs b/src/core/srcbc/crypto/CipherKeyGenerator.cs new file mode 100644 index 0000000..afd1982 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/CryptoException.cs b/src/core/srcbc/crypto/CryptoException.cs new file mode 100644 index 0000000..644bbb9 --- /dev/null +++ b/src/core/srcbc/crypto/CryptoException.cs @@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + public abstract class CryptoException + : Exception + { + protected CryptoException() + { + } + + protected CryptoException( + string message) + : base(message) + { + } + + protected CryptoException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/src/core/srcbc/crypto/DataLengthException.cs b/src/core/srcbc/crypto/DataLengthException.cs new file mode 100644 index 0000000..a3476a0 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/IAsymmetricBlockCipher.cs b/src/core/srcbc/crypto/IAsymmetricBlockCipher.cs new file mode 100644 index 0000000..31c5a85 --- /dev/null +++ b/src/core/srcbc/crypto/IAsymmetricBlockCipher.cs @@ -0,0 +1,30 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + ///+ * doFinal leaves the MAC in the same state it was after the last init. + *
+ * @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/src/core/srcbc/crypto/ISigner.cs b/src/core/srcbc/crypto/ISigner.cs new file mode 100644 index 0000000..2e13ccb --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/ISignerWithRecovery.cs b/src/core/srcbc/crypto/ISignerWithRecovery.cs new file mode 100644 index 0000000..b8bd822 --- /dev/null +++ b/src/core/srcbc/crypto/ISignerWithRecovery.cs @@ -0,0 +1,28 @@ + +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(); + } +} diff --git a/src/core/srcbc/crypto/IStreamCipher.cs b/src/core/srcbc/crypto/IStreamCipher.cs new file mode 100644 index 0000000..2d0e536 --- /dev/null +++ b/src/core/srcbc/crypto/IStreamCipher.cs @@ -0,0 +1,45 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + ///+ * 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.
+ *+ * 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.
+ */ + 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() + { + int bits = dhParams.P.BitLength - 1; + + // TODO Should the generated numbers always have length 'P.BitLength - 1'? + this.privateValue = new BigInteger(bits, random).SetBit(bits - 1); + + return dhParams.G.ModPow(privateValue, dhParams.P); + } + + /** + * 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."); + } + + return message.ModPow(key.X, dhParams.P).Multiply(pub.Y.ModPow(privateValue, dhParams.P)).Mod(dhParams.P); + } + } +} diff --git a/src/core/srcbc/crypto/agreement/DHBasicAgreement.cs b/src/core/srcbc/crypto/agreement/DHBasicAgreement.cs new file mode 100644 index 0000000..3a2f3e2 --- /dev/null +++ b/src/core/srcbc/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. + *+ * 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.
+ */ + 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/src/core/srcbc/crypto/agreement/ECDHBasicAgreement.cs b/src/core/srcbc/crypto/agreement/ECDHBasicAgreement.cs new file mode 100644 index 0000000..162bfa8 --- /dev/null +++ b/src/core/srcbc/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 + { + private ECPrivateKeyParameters key; + + public void Init( + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + this.key = (ECPrivateKeyParameters) parameters; + } + + public virtual BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey; + ECPoint P = pub.Q.Multiply(key.D); + + // if ( p.IsInfinity ) throw new Exception("d*Q == infinity"); + + return P.X.ToBigInteger(); + } + } + +} diff --git a/src/core/srcbc/crypto/agreement/ECDHCBasicAgreement.cs b/src/core/srcbc/crypto/agreement/ECDHCBasicAgreement.cs new file mode 100644 index 0000000..3bfa3e3 --- /dev/null +++ b/src/core/srcbc/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). + *+ * 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!).
+ */ + 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/src/core/srcbc/crypto/agreement/ECDHWithKdfBasicAgreement.cs b/src/core/srcbc/crypto/agreement/ECDHWithKdfBasicAgreement.cs new file mode 100644 index 0000000..0b0f3d2 --- /dev/null +++ b/src/core/srcbc/crypto/agreement/ECDHWithKdfBasicAgreement.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto.Agreement.Kdf; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + public class ECDHWithKdfBasicAgreement + : ECDHBasicAgreement + { + private static readonly Hashtable algorithms = new Hashtable(); + + static ECDHWithKdfBasicAgreement() + { + algorithms.Add(NistObjectIdentifiers.IdAes128Cbc.Id, 128); + algorithms.Add(NistObjectIdentifiers.IdAes192Cbc.Id, 192); + algorithms.Add(NistObjectIdentifiers.IdAes256Cbc.Id, 256); + algorithms.Add(NistObjectIdentifiers.IdAes128Wrap.Id, 128); + algorithms.Add(NistObjectIdentifiers.IdAes192Wrap.Id, 192); + algorithms.Add(NistObjectIdentifiers.IdAes256Wrap.Id, 256); + algorithms.Add(PkcsObjectIdentifiers.IdAlgCms3DesWrap.Id, 192); + } + + private readonly string algorithm; + private readonly IDerivationFunction kdf; + + public ECDHWithKdfBasicAgreement( + string algorithm, + IDerivationFunction kdf) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (!algorithms.Contains(algorithm)) + throw new ArgumentException("Unknown algorithm", "algorithm"); + if (kdf == null) + throw new ArgumentNullException("kdf"); + + this.algorithm = algorithm; + this.kdf = kdf; + } + + public override BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + BigInteger result = base.CalculateAgreement(pubKey); + + int keySize = (int) algorithms[algorithm]; + + DHKdfParameters dhKdfParams = new DHKdfParameters( + new DerObjectIdentifier(algorithm), + keySize, + // TODO Fix the way bytes are derived from the secret + result.ToByteArrayUnsigned()); + + kdf.Init(dhKdfParams); + + byte[] keyBytes = new byte[keySize / 8]; + kdf.GenerateBytes(keyBytes, 0, keyBytes.Length); + + return new BigInteger(1, keyBytes); + } + } +} diff --git a/src/core/srcbc/crypto/agreement/kdf/DHKdfParameters.cs b/src/core/srcbc/crypto/agreement/kdf/DHKdfParameters.cs new file mode 100644 index 0000000..eee337e --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/agreement/kdf/DHKekGenerator.cs b/src/core/srcbc/crypto/agreement/kdf/DHKekGenerator.cs new file mode 100644 index 0000000..e2a00ad --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/agreement/kdf/ECDHKekGenerator.cs b/src/core/srcbc/crypto/agreement/kdf/ECDHKekGenerator.cs new file mode 100644 index 0000000..810153e --- /dev/null +++ b/src/core/srcbc/crypto/agreement/kdf/ECDHKekGenerator.cs @@ -0,0 +1,70 @@ +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) + { + // 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/src/core/srcbc/crypto/digests/GOST3411Digest.cs b/src/core/srcbc/crypto/digests/GOST3411Digest.cs new file mode 100644 index 0000000..9326d43 --- /dev/null +++ b/src/core/srcbc/crypto/digests/GOST3411Digest.cs @@ -0,0 +1,338 @@ +using System; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; + +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 = new byte[4][]; + + private byte[] xBuf = new byte[32]; + private int xBufOff; + private long byteCount; + + private readonly IBlockCipher cipher = new Gost28147Engine(); + + /** + * Standard constructor + */ + public Gost3411Digest() + { + // TODO Is it possible to declare multi-dimensional arrays as in Java? + for (int i = 0; i < 4; ++i) + { + C[i] = new byte[32]; + } + + cipher.Init(true, new ParametersWithSBox(null, Gost28147Engine.GetSBox("D-A"))); + + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Gost3411Digest(Gost3411Digest t) + : this() + { +// cipher.Init(true, new ParametersWithSBox(null, Gost28147Engine.GetSBox("D-A"))); +// +// 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 += 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() + { + LongToBytes(byteCount * 8, L, 0); // get length into L (byteCount * 8 = bitCount) + + 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; + } + } + + // TODO Refactor as utility function + private static void LongToBytes( + 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 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+ * NOTE: This algorithm is only included for backwards compatibility + * with legacy applications, it's not secure, don't use it for anything new!
+ */ + 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/src/core/srcbc/crypto/digests/MD5Digest.cs b/src/core/srcbc/crypto/digests/MD5Digest.cs new file mode 100644 index 0000000..e9385d6 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/digests/RipeMD128Digest.cs b/src/core/srcbc/crypto/digests/RipeMD128Digest.cs new file mode 100644 index 0000000..c263796 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/digests/RipeMD160Digest.cs b/src/core/srcbc/crypto/digests/RipeMD160Digest.cs new file mode 100644 index 0000000..3b4979c --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/digests/RipeMD256Digest.cs b/src/core/srcbc/crypto/digests/RipeMD256Digest.cs new file mode 100644 index 0000000..ccaa774 --- /dev/null +++ b/src/core/srcbc/crypto/digests/RipeMD256Digest.cs @@ -0,0 +1,409 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + ///Implementation of RipeMD256.
+ ///Note: this algorithm offers the same level of security as RipeMD128.
+ ///Implementation of RipeMD 320.
+ ///Note: this algorithm offers the same level of security as RipeMD160.
+ ///+ * 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 + *+ */ + public class Sha224Digest + : GeneralDigest + { + private const int DigestLength = 28; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[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++] = ((input[inOff] & 0xff) << 24) | ((input[inOff + 1] & 0xff) << 16) + | ((input[inOff + 2] & 0xff) << 8) | ((input[inOff + 3] & 0xff)); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)((uint) word >> 24); + outBytes[outOff + 1] = (byte)((uint) word >> 16); + outBytes[outOff + 2] = (byte)((uint) word >> 8); + outBytes[outOff + 3] = (byte)word; + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)((ulong) bitLength >> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + 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); + UnpackWord(H5, output, outOff + 16); + UnpackWord(H6, output, outOff + 20); + UnpackWord(H7, output, outOff + 24); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + /* SHA-224 initial hash value + */ + + unchecked + { + H1 = (int) 0xc1059ed8; + H2 = (int) 0x367cd507; + H3 = (int) 0x3070dd17; + H4 = (int) 0xf70e5939; + H5 = (int) 0xffc00b31; + H6 = (int) 0x68581511; + H7 = (int) 0x64f98fa7; + H8 = (int) 0xbefa4fa4; + } + + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + 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. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int h = H8; + + int t = 0; + for(int i = 0; i < 8; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + (int)K[t] + X[t++]; + d += h; + h += Sum0(a) + Maj(a, b, c); + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + (int)K[t] + X[t++]; + c += g; + g += Sum0(h) + Maj(h, a, b); + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + (int)K[t] + X[t++]; + b += f; + f += Sum0(g) + Maj(g, h, a); + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + (int)K[t] + X[t++]; + a += e; + e += Sum0(f) + Maj(f, g, h); + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + (int)K[t] + X[t++]; + h += d; + d += Sum0(e) + Maj(e, f, g); + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + (int)K[t] + X[t++]; + g += c; + c += Sum0(d) + Maj(d, e, f); + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + (int)K[t] + X[t++]; + f += b; + b += Sum0(c) + Maj(c, d, e); + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + (int)K[t] + X[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. + // + xOff = 0; + + Array.Clear(X, 0, 16); + } + + /* SHA-224 functions */ + private static int Ch( + int x, + int y, + int z) + { + return ((x & y) ^ ((~x) & z)); + } + + private static int Maj( + int x, + int y, + int z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + private static int Sum0( + int x) + { + return (((int)((uint)x >> 2)) | (x << 30)) ^ (((int)((uint)x >> 13)) | (x << 19)) ^ (((int)((uint)x >> 22)) | (x << 10)); + } + + private static int Sum1( + int x) + { + return (((int)((uint)x >> 6)) | (x << 26)) ^ (((int)((uint)x >> 11)) | (x << 21)) ^ (((int)((uint)x >> 25)) | (x << 7)); + } + + private static int Theta0( + int x) + { + return (((int)((uint)x >> 7)) | (x << 25)) ^ (((int)((uint)x >> 18)) | (x << 14)) ^ ((int)((uint)x >> 3)); + } + + private static int Theta1( + int x) + { + return (((int)((uint)x >> 17)) | (x << 15)) ^ (((int)((uint)x >> 19)) | (x << 13)) ^ ((int)((uint)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/src/core/srcbc/crypto/digests/Sha256Digest.cs b/src/core/srcbc/crypto/digests/Sha256Digest.cs new file mode 100644 index 0000000..de9c9da --- /dev/null +++ b/src/core/srcbc/crypto/digests/Sha256Digest.cs @@ -0,0 +1,310 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Draft FIPS 180-2 implementation of SHA-256. Note: As this is + * based on a draft this implementation is subject to change. + * + *
+ * block word digest + * SHA-1 512 32 160 + * SHA-256 512 32 256 + * SHA-384 1024 64 384 + * SHA-512 1024 64 512 + *+ */ + public class Sha256Digest + : GeneralDigest + { + private const int DigestLength = 32; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[64]; + private int xOff; + + public Sha256Digest() + { + Reset(); + } + + /** + * 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++] = ((input[inOff] & 0xff) << 24) | ((input[inOff + 1] & 0xff) << 16) + | ((input[inOff + 2] & 0xff) << 8) | ((input[inOff + 3] & 0xff)); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)((uint) word >> 24); + outBytes[outOff + 1] = (byte)((uint) word >> 16); + outBytes[outOff + 2] = (byte)((uint) word >> 8); + outBytes[outOff + 3] = (byte)word; + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)((ulong) bitLength >> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + 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); + UnpackWord(H5, output, outOff + 16); + UnpackWord(H6, output, outOff + 20); + UnpackWord(H7, output, outOff + 24); + UnpackWord(H8, output, outOff + 28); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + /* SHA-256 initial hash value + * The first 32 bits of the fractional parts of the square roots + * of the first eight prime numbers + */ + unchecked + { + H1 = (int) 0x6a09e667; + H2 = (int) 0xbb67ae85; + H3 = (int) 0x3c6ef372; + H4 = (int) 0xa54ff53a; + H5 = (int) 0x510e527f; + H6 = (int) 0x9b05688c; + H7 = (int) 0x1f83d9ab; + H8 = (int) 0x5be0cd19; + } + + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + 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. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int 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 = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t++]; + c += g; + g += Sum0(h) + Maj(h, a, b); + + // 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 = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t++]; + a += e; + e += Sum0(f) + Maj(f, g, h); + + // 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 = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t++]; + g += c; + c += Sum0(d) + Maj(d, e, f); + + // 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 = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + X[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. + // + xOff = 0; + + Array.Clear(X, 0, 16); + } + + /* SHA-256 functions */ + private static int Ch( + int x, + int y, + int z) + { + return ((x & y) ^ ((~x) & z)); + } + + private static int Maj( + int x, + int y, + int z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + private static int Sum0( + int x) + { + return (((int)((uint)x >> 2)) | (x << 30)) ^ (((int)((uint)x >> 13)) | (x << 19)) ^ (((int)((uint)x >> 22)) | (x << 10)); + } + + private static int Sum1( + int x) + { + return (((int)((uint)x >> 6)) | (x << 26)) ^ (((int)((uint)x >> 11)) | (x << 21)) ^ (((int)((uint)x >> 25)) | (x << 7)); + } + + private static int Theta0( + int x) + { + return (((int)((uint)x >> 7)) | (x << 25)) ^ (((int)((uint)x >> 18)) | (x << 14)) ^ ((int)((uint)x >> 3)); + } + + private static int Theta1( + int x) + { + return (((int)((uint)x >> 17)) | (x << 15)) ^ (((int)((uint)x >> 19)) | (x << 13)) ^ ((int)((uint)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) + */ + internal static readonly int[] K = { + unchecked ((int) 0x428a2f98), unchecked ((int) 0x71374491), + unchecked ((int) 0xb5c0fbcf), unchecked ((int) 0xe9b5dba5), + unchecked ((int) 0x3956c25b), unchecked ((int) 0x59f111f1), + unchecked ((int) 0x923f82a4), unchecked ((int) 0xab1c5ed5), + unchecked ((int) 0xd807aa98), unchecked ((int) 0x12835b01), + unchecked ((int) 0x243185be), unchecked ((int) 0x550c7dc3), + unchecked ((int) 0x72be5d74), unchecked ((int) 0x80deb1fe), + unchecked ((int) 0x9bdc06a7), unchecked ((int) 0xc19bf174), + unchecked ((int) 0xe49b69c1), unchecked ((int) 0xefbe4786), + unchecked ((int) 0x0fc19dc6), unchecked ((int) 0x240ca1cc), + unchecked ((int) 0x2de92c6f), unchecked ((int) 0x4a7484aa), + unchecked ((int) 0x5cb0a9dc), unchecked ((int) 0x76f988da), + unchecked ((int) 0x983e5152), unchecked ((int) 0xa831c66d), + unchecked ((int) 0xb00327c8), unchecked ((int) 0xbf597fc7), + unchecked ((int) 0xc6e00bf3), unchecked ((int) 0xd5a79147), + unchecked ((int) 0x06ca6351), unchecked ((int) 0x14292967), + unchecked ((int) 0x27b70a85), unchecked ((int) 0x2e1b2138), + unchecked ((int) 0x4d2c6dfc), unchecked ((int) 0x53380d13), + unchecked ((int) 0x650a7354), unchecked ((int) 0x766a0abb), + unchecked ((int) 0x81c2c92e), unchecked ((int) 0x92722c85), + unchecked ((int) 0xa2bfe8a1), unchecked ((int) 0xa81a664b), + unchecked ((int) 0xc24b8b70), unchecked ((int) 0xc76c51a3), + unchecked ((int) 0xd192e819), unchecked ((int) 0xd6990624), + unchecked ((int) 0xf40e3585), unchecked ((int) 0x106aa070), + unchecked ((int) 0x19a4c116), unchecked ((int) 0x1e376c08), + unchecked ((int) 0x2748774c), unchecked ((int) 0x34b0bcb5), + unchecked ((int) 0x391c0cb3), unchecked ((int) 0x4ed8aa4a), + unchecked ((int) 0x5b9cca4f), unchecked ((int) 0x682e6ff3), + unchecked ((int) 0x748f82ee), unchecked ((int) 0x78a5636f), + unchecked ((int) 0x84c87814), unchecked ((int) 0x8cc70208), + unchecked ((int) 0x90befffa), unchecked ((int) 0xa4506ceb), + unchecked ((int) 0xbef9a3f7), unchecked ((int) 0xc67178f2) + }; + } +} diff --git a/src/core/srcbc/crypto/digests/Sha384Digest.cs b/src/core/srcbc/crypto/digests/Sha384Digest.cs new file mode 100644 index 0000000..7fe6daf --- /dev/null +++ b/src/core/srcbc/crypto/digests/Sha384Digest.cs @@ -0,0 +1,85 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Draft FIPS 180-2 implementation of SHA-384. Note: As this is + * based on a draft this implementation is subject to change. + * + *
+ * block word digest + * SHA-1 512 32 160 + * SHA-256 512 32 256 + * SHA-384 1024 64 384 + * SHA-512 1024 64 512 + *+ */ + 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(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 16); + UnpackWord(H4, output, outOff + 24); + UnpackWord(H5, output, outOff + 32); + UnpackWord(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 = unchecked((long) 0xcbbb9d5dc1059ed8L); + H2 = unchecked((long) 0x629a292a367cd507L); + H3 = unchecked((long) 0x9159015a3070dd17L); + H4 = unchecked((long) 0x152fecd8f70e5939L); + H5 = unchecked((long) 0x67332667ffc00b31L); + H6 = unchecked((long) 0x8eb44a8768581511L); + H7 = unchecked((long) 0xdb0c2e0d64f98fa7L); + H8 = unchecked((long) 0x47b5481dbefa4fa4L); + } + } +} diff --git a/src/core/srcbc/crypto/digests/Sha512Digest.cs b/src/core/srcbc/crypto/digests/Sha512Digest.cs new file mode 100644 index 0000000..4c47f4b --- /dev/null +++ b/src/core/srcbc/crypto/digests/Sha512Digest.cs @@ -0,0 +1,88 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Draft FIPS 180-2 implementation of SHA-512. Note: As this is + * based on a draft this implementation is subject to change. + * + *
+ * block word digest + * SHA-1 512 32 160 + * SHA-256 512 32 256 + * SHA-384 1024 64 384 + * SHA-512 1024 64 512 + *+ */ + 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(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 16); + UnpackWord(H4, output, outOff + 24); + UnpackWord(H5, output, outOff + 32); + UnpackWord(H6, output, outOff + 40); + UnpackWord(H7, output, outOff + 48); + UnpackWord(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 = unchecked((long) 0x6a09e667f3bcc908L); + H2 = unchecked((long) 0xbb67ae8584caa73bL); + H3 = unchecked((long) 0x3c6ef372fe94f82bL); + H4 = unchecked((long) 0xa54ff53a5f1d36f1L); + H5 = unchecked((long) 0x510e527fade682d1L); + H6 = unchecked((long) 0x9b05688c2b3e6c1fL); + H7 = unchecked((long) 0x1f83d9abfb41bd6bL); + H8 = unchecked((long) 0x5be0cd19137e2179L); + } + } +} diff --git a/src/core/srcbc/crypto/digests/ShortenedDigest.cs b/src/core/srcbc/crypto/digests/ShortenedDigest.cs new file mode 100644 index 0000000..de0d325 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/digests/TigerDigest.cs b/src/core/srcbc/crypto/digests/TigerDigest.cs new file mode 100644 index 0000000..16a2b1c --- /dev/null +++ b/src/core/srcbc/crypto/digests/TigerDigest.cs @@ -0,0 +1,868 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of Tiger based on: + * + * http://www.cs.technion.ac.il/~biham/Reports/Tiger + */ + 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/src/core/srcbc/crypto/digests/WhirlpoolDigest.cs b/src/core/srcbc/crypto/digests/WhirlpoolDigest.cs new file mode 100644 index 0000000..5ac0ff6 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/encodings/ISO9796d1Encoding.cs b/src/core/srcbc/crypto/encodings/ISO9796d1Encoding.cs new file mode 100644 index 0000000..02619d5 --- /dev/null +++ b/src/core/srcbc/crypto/encodings/ISO9796d1Encoding.cs @@ -0,0 +1,253 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +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 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; + + 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); + + bitSize = kParam.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; + + 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/src/core/srcbc/crypto/encodings/OaepEncoding.cs b/src/core/srcbc/crypto/encodings/OaepEncoding.cs new file mode 100644 index 0000000..1d00171 --- /dev/null +++ b/src/core/srcbc/crypto/encodings/OaepEncoding.cs @@ -0,0 +1,334 @@ +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 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.engine = cipher; + this.hash = hash; + 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 = null; + + // + // 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] == 1 || 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[defHash.Length]; + byte[] C = new byte[4]; + int counter = 0; + + hash.Reset(); + + do + { + ItoOSP(counter, C); + + hash.BlockUpdate(Z, zOff, zLen); + hash.BlockUpdate(C, 0, C.Length); + hash.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * defHash.Length, defHash.Length); + } + while (++counter < (length / defHash.Length)); + + if ((counter * defHash.Length) < length) + { + ItoOSP(counter, C); + + hash.BlockUpdate(Z, zOff, zLen); + hash.BlockUpdate(C, 0, C.Length); + hash.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * defHash.Length, mask.Length - (counter * defHash.Length)); + } + + return mask; + } + } +} + diff --git a/src/core/srcbc/crypto/encodings/Pkcs1Encoding.cs b/src/core/srcbc/crypto/encodings/Pkcs1Encoding.cs new file mode 100644 index 0000000..b813728 --- /dev/null +++ b/src/core/srcbc/crypto/encodings/Pkcs1Encoding.cs @@ -0,0 +1,229 @@ +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 + *
+ * The static property is checked during construction of the encoding object, it is set to + * true by default. + *
+ */ + 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) + { + 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/src/core/srcbc/crypto/engines/AesEngine.cs b/src/core/srcbc/crypto/engines/AesEngine.cs new file mode 100644 index 0000000..9b802b1 --- /dev/null +++ b/src/core/srcbc/crypto/engines/AesEngine.cs @@ -0,0 +1,550 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + + /** + * an implementation of the AES (Rijndael), from FIPS-197. + *+ * For further details see: http://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * 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. + *
+ *+ * This file contains the middle performance version with 2Kbytes of static tables for round precomputation. + *
+ */ + 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 int[] 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 int[] T0 = + { + unchecked((int) 0xa56363c6), unchecked((int) 0x847c7cf8), unchecked((int) 0x997777ee), unchecked((int) 0x8d7b7bf6), unchecked((int) 0x0df2f2ff), + unchecked((int) 0xbd6b6bd6), unchecked((int) 0xb16f6fde), unchecked((int) 0x54c5c591), unchecked((int) 0x50303060), unchecked((int) 0x03010102), + unchecked((int) 0xa96767ce), unchecked((int) 0x7d2b2b56), unchecked((int) 0x19fefee7), unchecked((int) 0x62d7d7b5), unchecked((int) 0xe6abab4d), + unchecked((int) 0x9a7676ec), unchecked((int) 0x45caca8f), unchecked((int) 0x9d82821f), unchecked((int) 0x40c9c989), unchecked((int) 0x877d7dfa), + unchecked((int) 0x15fafaef), unchecked((int) 0xeb5959b2), unchecked((int) 0xc947478e), unchecked((int) 0x0bf0f0fb), unchecked((int) 0xecadad41), + unchecked((int) 0x67d4d4b3), unchecked((int) 0xfda2a25f), unchecked((int) 0xeaafaf45), unchecked((int) 0xbf9c9c23), unchecked((int) 0xf7a4a453), + unchecked((int) 0x967272e4), unchecked((int) 0x5bc0c09b), unchecked((int) 0xc2b7b775), unchecked((int) 0x1cfdfde1), unchecked((int) 0xae93933d), + unchecked((int) 0x6a26264c), unchecked((int) 0x5a36366c), unchecked((int) 0x413f3f7e), unchecked((int) 0x02f7f7f5), unchecked((int) 0x4fcccc83), + unchecked((int) 0x5c343468), unchecked((int) 0xf4a5a551), unchecked((int) 0x34e5e5d1), unchecked((int) 0x08f1f1f9), unchecked((int) 0x937171e2), + unchecked((int) 0x73d8d8ab), unchecked((int) 0x53313162), unchecked((int) 0x3f15152a), unchecked((int) 0x0c040408), unchecked((int) 0x52c7c795), + unchecked((int) 0x65232346), unchecked((int) 0x5ec3c39d), unchecked((int) 0x28181830), unchecked((int) 0xa1969637), unchecked((int) 0x0f05050a), + unchecked((int) 0xb59a9a2f), unchecked((int) 0x0907070e), unchecked((int) 0x36121224), unchecked((int) 0x9b80801b), unchecked((int) 0x3de2e2df), + unchecked((int) 0x26ebebcd), unchecked((int) 0x6927274e), unchecked((int) 0xcdb2b27f), unchecked((int) 0x9f7575ea), unchecked((int) 0x1b090912), + unchecked((int) 0x9e83831d), unchecked((int) 0x742c2c58), unchecked((int) 0x2e1a1a34), unchecked((int) 0x2d1b1b36), unchecked((int) 0xb26e6edc), + unchecked((int) 0xee5a5ab4), unchecked((int) 0xfba0a05b), unchecked((int) 0xf65252a4), unchecked((int) 0x4d3b3b76), unchecked((int) 0x61d6d6b7), + unchecked((int) 0xceb3b37d), unchecked((int) 0x7b292952), unchecked((int) 0x3ee3e3dd), unchecked((int) 0x712f2f5e), unchecked((int) 0x97848413), + unchecked((int) 0xf55353a6), unchecked((int) 0x68d1d1b9), unchecked((int) 0x00000000), unchecked((int) 0x2cededc1), unchecked((int) 0x60202040), + unchecked((int) 0x1ffcfce3), unchecked((int) 0xc8b1b179), unchecked((int) 0xed5b5bb6), unchecked((int) 0xbe6a6ad4), unchecked((int) 0x46cbcb8d), + unchecked((int) 0xd9bebe67), unchecked((int) 0x4b393972), unchecked((int) 0xde4a4a94), unchecked((int) 0xd44c4c98), unchecked((int) 0xe85858b0), + unchecked((int) 0x4acfcf85), unchecked((int) 0x6bd0d0bb), unchecked((int) 0x2aefefc5), unchecked((int) 0xe5aaaa4f), unchecked((int) 0x16fbfbed), + unchecked((int) 0xc5434386), unchecked((int) 0xd74d4d9a), unchecked((int) 0x55333366), unchecked((int) 0x94858511), unchecked((int) 0xcf45458a), + unchecked((int) 0x10f9f9e9), unchecked((int) 0x06020204), unchecked((int) 0x817f7ffe), unchecked((int) 0xf05050a0), unchecked((int) 0x443c3c78), + unchecked((int) 0xba9f9f25), unchecked((int) 0xe3a8a84b), unchecked((int) 0xf35151a2), unchecked((int) 0xfea3a35d), unchecked((int) 0xc0404080), + unchecked((int) 0x8a8f8f05), unchecked((int) 0xad92923f), unchecked((int) 0xbc9d9d21), unchecked((int) 0x48383870), unchecked((int) 0x04f5f5f1), + unchecked((int) 0xdfbcbc63), unchecked((int) 0xc1b6b677), unchecked((int) 0x75dadaaf), unchecked((int) 0x63212142), unchecked((int) 0x30101020), + unchecked((int) 0x1affffe5), unchecked((int) 0x0ef3f3fd), unchecked((int) 0x6dd2d2bf), unchecked((int) 0x4ccdcd81), unchecked((int) 0x140c0c18), + unchecked((int) 0x35131326), unchecked((int) 0x2fececc3), unchecked((int) 0xe15f5fbe), unchecked((int) 0xa2979735), unchecked((int) 0xcc444488), + unchecked((int) 0x3917172e), unchecked((int) 0x57c4c493), unchecked((int) 0xf2a7a755), unchecked((int) 0x827e7efc), unchecked((int) 0x473d3d7a), + unchecked((int) 0xac6464c8), unchecked((int) 0xe75d5dba), unchecked((int) 0x2b191932), unchecked((int) 0x957373e6), unchecked((int) 0xa06060c0), + unchecked((int) 0x98818119), unchecked((int) 0xd14f4f9e), unchecked((int) 0x7fdcdca3), unchecked((int) 0x66222244), unchecked((int) 0x7e2a2a54), + unchecked((int) 0xab90903b), unchecked((int) 0x8388880b), unchecked((int) 0xca46468c), unchecked((int) 0x29eeeec7), unchecked((int) 0xd3b8b86b), + unchecked((int) 0x3c141428), unchecked((int) 0x79dedea7), unchecked((int) 0xe25e5ebc), unchecked((int) 0x1d0b0b16), unchecked((int) 0x76dbdbad), + unchecked((int) 0x3be0e0db), unchecked((int) 0x56323264), unchecked((int) 0x4e3a3a74), unchecked((int) 0x1e0a0a14), unchecked((int) 0xdb494992), + unchecked((int) 0x0a06060c), unchecked((int) 0x6c242448), unchecked((int) 0xe45c5cb8), unchecked((int) 0x5dc2c29f), unchecked((int) 0x6ed3d3bd), + unchecked((int) 0xefacac43), unchecked((int) 0xa66262c4), unchecked((int) 0xa8919139), unchecked((int) 0xa4959531), unchecked((int) 0x37e4e4d3), + unchecked((int) 0x8b7979f2), unchecked((int) 0x32e7e7d5), unchecked((int) 0x43c8c88b), unchecked((int) 0x5937376e), unchecked((int) 0xb76d6dda), + unchecked((int) 0x8c8d8d01), unchecked((int) 0x64d5d5b1), unchecked((int) 0xd24e4e9c), unchecked((int) 0xe0a9a949), unchecked((int) 0xb46c6cd8), + unchecked((int) 0xfa5656ac), unchecked((int) 0x07f4f4f3), unchecked((int) 0x25eaeacf), unchecked((int) 0xaf6565ca), unchecked((int) 0x8e7a7af4), + unchecked((int) 0xe9aeae47), unchecked((int) 0x18080810), unchecked((int) 0xd5baba6f), unchecked((int) 0x887878f0), unchecked((int) 0x6f25254a), + unchecked((int) 0x722e2e5c), unchecked((int) 0x241c1c38), unchecked((int) 0xf1a6a657), unchecked((int) 0xc7b4b473), unchecked((int) 0x51c6c697), + unchecked((int) 0x23e8e8cb), unchecked((int) 0x7cdddda1), unchecked((int) 0x9c7474e8), unchecked((int) 0x211f1f3e), unchecked((int) 0xdd4b4b96), + unchecked((int) 0xdcbdbd61), unchecked((int) 0x868b8b0d), unchecked((int) 0x858a8a0f), unchecked((int) 0x907070e0), unchecked((int) 0x423e3e7c), + unchecked((int) 0xc4b5b571), unchecked((int) 0xaa6666cc), unchecked((int) 0xd8484890), unchecked((int) 0x05030306), unchecked((int) 0x01f6f6f7), + unchecked((int) 0x120e0e1c), unchecked((int) 0xa36161c2), unchecked((int) 0x5f35356a), unchecked((int) 0xf95757ae), unchecked((int) 0xd0b9b969), + unchecked((int) 0x91868617), unchecked((int) 0x58c1c199), unchecked((int) 0x271d1d3a), unchecked((int) 0xb99e9e27), unchecked((int) 0x38e1e1d9), + unchecked((int) 0x13f8f8eb), unchecked((int) 0xb398982b), unchecked((int) 0x33111122), unchecked((int) 0xbb6969d2), unchecked((int) 0x70d9d9a9), + unchecked((int) 0x898e8e07), unchecked((int) 0xa7949433), unchecked((int) 0xb69b9b2d), unchecked((int) 0x221e1e3c), unchecked((int) 0x92878715), + unchecked((int) 0x20e9e9c9), unchecked((int) 0x49cece87), unchecked((int) 0xff5555aa), unchecked((int) 0x78282850), unchecked((int) 0x7adfdfa5), + unchecked((int) 0x8f8c8c03), unchecked((int) 0xf8a1a159), unchecked((int) 0x80898909), unchecked((int) 0x170d0d1a), unchecked((int) 0xdabfbf65), + unchecked((int) 0x31e6e6d7), unchecked((int) 0xc6424284), unchecked((int) 0xb86868d0), unchecked((int) 0xc3414182), unchecked((int) 0xb0999929), + unchecked((int) 0x772d2d5a), unchecked((int) 0x110f0f1e), unchecked((int) 0xcbb0b07b), unchecked((int) 0xfc5454a8), unchecked((int) 0xd6bbbb6d), + unchecked((int) 0x3a16162c) + }; + + private static readonly int[] Tinv0 = + { + unchecked((int) 0x50a7f451), unchecked((int) 0x5365417e), unchecked((int) 0xc3a4171a), unchecked((int) 0x965e273a), unchecked((int) 0xcb6bab3b), + unchecked((int) 0xf1459d1f), unchecked((int) 0xab58faac), unchecked((int) 0x9303e34b), unchecked((int) 0x55fa3020), unchecked((int) 0xf66d76ad), + unchecked((int) 0x9176cc88), unchecked((int) 0x254c02f5), unchecked((int) 0xfcd7e54f), unchecked((int) 0xd7cb2ac5), unchecked((int) 0x80443526), + unchecked((int) 0x8fa362b5), unchecked((int) 0x495ab1de), unchecked((int) 0x671bba25), unchecked((int) 0x980eea45), unchecked((int) 0xe1c0fe5d), + unchecked((int) 0x02752fc3), unchecked((int) 0x12f04c81), unchecked((int) 0xa397468d), unchecked((int) 0xc6f9d36b), unchecked((int) 0xe75f8f03), + unchecked((int) 0x959c9215), unchecked((int) 0xeb7a6dbf), unchecked((int) 0xda595295), unchecked((int) 0x2d83bed4), unchecked((int) 0xd3217458), + unchecked((int) 0x2969e049), unchecked((int) 0x44c8c98e), unchecked((int) 0x6a89c275), unchecked((int) 0x78798ef4), unchecked((int) 0x6b3e5899), + unchecked((int) 0xdd71b927), unchecked((int) 0xb64fe1be), unchecked((int) 0x17ad88f0), unchecked((int) 0x66ac20c9), unchecked((int) 0xb43ace7d), + unchecked((int) 0x184adf63), unchecked((int) 0x82311ae5), unchecked((int) 0x60335197), unchecked((int) 0x457f5362), unchecked((int) 0xe07764b1), + unchecked((int) 0x84ae6bbb), unchecked((int) 0x1ca081fe), unchecked((int) 0x942b08f9), unchecked((int) 0x58684870), unchecked((int) 0x19fd458f), + unchecked((int) 0x876cde94), unchecked((int) 0xb7f87b52), unchecked((int) 0x23d373ab), unchecked((int) 0xe2024b72), unchecked((int) 0x578f1fe3), + unchecked((int) 0x2aab5566), unchecked((int) 0x0728ebb2), unchecked((int) 0x03c2b52f), unchecked((int) 0x9a7bc586), unchecked((int) 0xa50837d3), + unchecked((int) 0xf2872830), unchecked((int) 0xb2a5bf23), unchecked((int) 0xba6a0302), unchecked((int) 0x5c8216ed), unchecked((int) 0x2b1ccf8a), + unchecked((int) 0x92b479a7), unchecked((int) 0xf0f207f3), unchecked((int) 0xa1e2694e), unchecked((int) 0xcdf4da65), unchecked((int) 0xd5be0506), + unchecked((int) 0x1f6234d1), unchecked((int) 0x8afea6c4), unchecked((int) 0x9d532e34), unchecked((int) 0xa055f3a2), unchecked((int) 0x32e18a05), + unchecked((int) 0x75ebf6a4), unchecked((int) 0x39ec830b), unchecked((int) 0xaaef6040), unchecked((int) 0x069f715e), unchecked((int) 0x51106ebd), + unchecked((int) 0xf98a213e), unchecked((int) 0x3d06dd96), unchecked((int) 0xae053edd), unchecked((int) 0x46bde64d), unchecked((int) 0xb58d5491), + unchecked((int) 0x055dc471), unchecked((int) 0x6fd40604), unchecked((int) 0xff155060), unchecked((int) 0x24fb9819), unchecked((int) 0x97e9bdd6), + unchecked((int) 0xcc434089), unchecked((int) 0x779ed967), unchecked((int) 0xbd42e8b0), unchecked((int) 0x888b8907), unchecked((int) 0x385b19e7), + unchecked((int) 0xdbeec879), unchecked((int) 0x470a7ca1), unchecked((int) 0xe90f427c), unchecked((int) 0xc91e84f8), unchecked((int) 0x00000000), + unchecked((int) 0x83868009), unchecked((int) 0x48ed2b32), unchecked((int) 0xac70111e), unchecked((int) 0x4e725a6c), unchecked((int) 0xfbff0efd), + unchecked((int) 0x5638850f), unchecked((int) 0x1ed5ae3d), unchecked((int) 0x27392d36), unchecked((int) 0x64d90f0a), unchecked((int) 0x21a65c68), + unchecked((int) 0xd1545b9b), unchecked((int) 0x3a2e3624), unchecked((int) 0xb1670a0c), unchecked((int) 0x0fe75793), unchecked((int) 0xd296eeb4), + unchecked((int) 0x9e919b1b), unchecked((int) 0x4fc5c080), unchecked((int) 0xa220dc61), unchecked((int) 0x694b775a), unchecked((int) 0x161a121c), + unchecked((int) 0x0aba93e2), unchecked((int) 0xe52aa0c0), unchecked((int) 0x43e0223c), unchecked((int) 0x1d171b12), unchecked((int) 0x0b0d090e), + unchecked((int) 0xadc78bf2), unchecked((int) 0xb9a8b62d), unchecked((int) 0xc8a91e14), unchecked((int) 0x8519f157), unchecked((int) 0x4c0775af), + unchecked((int) 0xbbdd99ee), unchecked((int) 0xfd607fa3), unchecked((int) 0x9f2601f7), unchecked((int) 0xbcf5725c), unchecked((int) 0xc53b6644), + unchecked((int) 0x347efb5b), unchecked((int) 0x7629438b), unchecked((int) 0xdcc623cb), unchecked((int) 0x68fcedb6), unchecked((int) 0x63f1e4b8), + unchecked((int) 0xcadc31d7), unchecked((int) 0x10856342), unchecked((int) 0x40229713), unchecked((int) 0x2011c684), unchecked((int) 0x7d244a85), + unchecked((int) 0xf83dbbd2), unchecked((int) 0x1132f9ae), unchecked((int) 0x6da129c7), unchecked((int) 0x4b2f9e1d), unchecked((int) 0xf330b2dc), + unchecked((int) 0xec52860d), unchecked((int) 0xd0e3c177), unchecked((int) 0x6c16b32b), unchecked((int) 0x99b970a9), unchecked((int) 0xfa489411), + unchecked((int) 0x2264e947), unchecked((int) 0xc48cfca8), unchecked((int) 0x1a3ff0a0), unchecked((int) 0xd82c7d56), unchecked((int) 0xef903322), + unchecked((int) 0xc74e4987), unchecked((int) 0xc1d138d9), unchecked((int) 0xfea2ca8c), unchecked((int) 0x360bd498), unchecked((int) 0xcf81f5a6), + unchecked((int) 0x28de7aa5), unchecked((int) 0x268eb7da), unchecked((int) 0xa4bfad3f), unchecked((int) 0xe49d3a2c), unchecked((int) 0x0d927850), + unchecked((int) 0x9bcc5f6a), unchecked((int) 0x62467e54), unchecked((int) 0xc2138df6), unchecked((int) 0xe8b8d890), unchecked((int) 0x5ef7392e), + unchecked((int) 0xf5afc382), unchecked((int) 0xbe805d9f), unchecked((int) 0x7c93d069), unchecked((int) 0xa92dd56f), unchecked((int) 0xb31225cf), + unchecked((int) 0x3b99acc8), unchecked((int) 0xa77d1810), unchecked((int) 0x6e639ce8), unchecked((int) 0x7bbb3bdb), unchecked((int) 0x097826cd), + unchecked((int) 0xf418596e), unchecked((int) 0x01b79aec), unchecked((int) 0xa89a4f83), unchecked((int) 0x656e95e6), unchecked((int) 0x7ee6ffaa), + unchecked((int) 0x08cfbc21), unchecked((int) 0xe6e815ef), unchecked((int) 0xd99be7ba), unchecked((int) 0xce366f4a), unchecked((int) 0xd4099fea), + unchecked((int) 0xd67cb029), unchecked((int) 0xafb2a431), unchecked((int) 0x31233f2a), unchecked((int) 0x3094a5c6), unchecked((int) 0xc066a235), + unchecked((int) 0x37bc4e74), unchecked((int) 0xa6ca82fc), unchecked((int) 0xb0d090e0), unchecked((int) 0x15d8a733), unchecked((int) 0x4a9804f1), + unchecked((int) 0xf7daec41), unchecked((int) 0x0e50cd7f), unchecked((int) 0x2ff69117), unchecked((int) 0x8dd64d76), unchecked((int) 0x4db0ef43), + unchecked((int) 0x544daacc), unchecked((int) 0xdf0496e4), unchecked((int) 0xe3b5d19e), unchecked((int) 0x1b886a4c), unchecked((int) 0xb81f2cc1), + unchecked((int) 0x7f516546), unchecked((int) 0x04ea5e9d), unchecked((int) 0x5d358c01), unchecked((int) 0x737487fa), unchecked((int) 0x2e410bfb), + unchecked((int) 0x5a1d67b3), unchecked((int) 0x52d2db92), unchecked((int) 0x335610e9), unchecked((int) 0x1347d66d), unchecked((int) 0x8c61d79a), + unchecked((int) 0x7a0ca137), unchecked((int) 0x8e14f859), unchecked((int) 0x893c13eb), unchecked((int) 0xee27a9ce), unchecked((int) 0x35c961b7), + unchecked((int) 0xede51ce1), unchecked((int) 0x3cb1477a), unchecked((int) 0x59dfd29c), unchecked((int) 0x3f73f255), unchecked((int) 0x79ce1418), + unchecked((int) 0xbf37c773), unchecked((int) 0xeacdf753), unchecked((int) 0x5baafd5f), unchecked((int) 0x146f3ddf), unchecked((int) 0x86db4478), + unchecked((int) 0x81f3afca), unchecked((int) 0x3ec468b9), unchecked((int) 0x2c342438), unchecked((int) 0x5f40a3c2), unchecked((int) 0x72c31d16), + unchecked((int) 0x0c25e2bc), unchecked((int) 0x8b493c28), unchecked((int) 0x41950dff), unchecked((int) 0x7101a839), unchecked((int) 0xdeb30c08), + unchecked((int) 0x9ce4b4d8), unchecked((int) 0x90c15664), unchecked((int) 0x6184cb7b), unchecked((int) 0x70b632d5), unchecked((int) 0x745c6c48), + unchecked((int) 0x4257b8d0) + }; + + private int Shift( + int r, + int shift) + { + return ((int)(((uint) r >> shift) | (uint)(r << (32 - shift)))); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const int m1 = unchecked((int) 0x80808080); + private const int m2 = unchecked((int) 0x7f7f7f7f); + private const int m3 = unchecked((int) 0x0000001b); + + private int FFmulX( + int x) + { + return ((int) (((x & m2) << 1) ^ (( (uint) (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 int Inv_Mcol( + int x) + { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + private int SubWord(int x) { + return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | 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 int[,] 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 + int[,] W = new int[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] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + int 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 int[,] WorkingKey; + private int 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) + { + int index = off; + + C0 = (bytes[index++] & 0xff); + C0 |= (bytes[index++] & 0xff) << 8; + C0 |= (bytes[index++] & 0xff) << 16; + C0 |= bytes[index++] << 24; + + C1 = (bytes[index++] & 0xff); + C1 |= (bytes[index++] & 0xff) << 8; + C1 |= (bytes[index++] & 0xff) << 16; + C1 |= bytes[index++] << 24; + + C2 = (bytes[index++] & 0xff); + C2 |= (bytes[index++] & 0xff) << 8; + C2 |= (bytes[index++] & 0xff) << 16; + C2 |= bytes[index++] << 24; + + C3 = (bytes[index++] & 0xff); + C3 |= (bytes[index++] & 0xff) << 8; + C3 |= (bytes[index++] & 0xff) << 16; + C3 |= bytes[index++] << 24; + } + + private void PackBlock( + byte[] bytes, + int off) + { + int index = off; + + bytes[index++] = (byte)C0; + bytes[index++] = (byte)(C0 >> 8); + bytes[index++] = (byte)(C0 >> 16); + bytes[index++] = (byte)(C0 >> 24); + + bytes[index++] = (byte)C1; + bytes[index++] = (byte)(C1 >> 8); + bytes[index++] = (byte)(C1 >> 16); + bytes[index++] = (byte)(C1 >> 24); + + bytes[index++] = (byte)C2; + bytes[index++] = (byte)(C2 >> 8); + bytes[index++] = (byte)(C2 >> 16); + bytes[index++] = (byte)(C2 >> 24); + + bytes[index++] = (byte)C3; + bytes[index++] = (byte)(C3 >> 8); + bytes[index++] = (byte)(C3 >> 16); + bytes[index++] = (byte)(C3 >> 24); + } + + private void EncryptBlock( + int[,] KW) + { + int 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 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r,0]; + C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r,1]; + C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r,2]; + C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r,3]; + } + + private void DecryptBlock( + int[,] KW) + { + int r, 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 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0,0]; + C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0,1]; + C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0,2]; + C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0,3]; + } + } +} diff --git a/src/core/srcbc/crypto/engines/AesFastEngine.cs b/src/core/srcbc/crypto/engines/AesFastEngine.cs new file mode 100644 index 0000000..22ef651 --- /dev/null +++ b/src/core/srcbc/crypto/engines/AesFastEngine.cs @@ -0,0 +1,865 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * an implementation of the AES (Rijndael)), from FIPS-197. + *+ * For further details see: http://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * 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 + *
+ *+ * This file contains the fast version with 8Kbytes of static tables for round precomputation + *
+ */ + public class AesFastEngine + : IBlockCipher + { + // The S box + private static readonly byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, + (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, + (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, + (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, + (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, + (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, + (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, + (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, + (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, + (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, + (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, + (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, + (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, + (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, + (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, + (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, + (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + // The inverse S-box + private static readonly byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, + (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, + (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, + (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, + (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, + (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, + (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, + (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, + (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, + (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, + (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, + (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, + (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, + (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, + (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, + (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, + (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static readonly int[] 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 int[] T0 = + { + unchecked((int) 0xa56363c6), unchecked((int) 0x847c7cf8), unchecked((int) 0x997777ee), unchecked((int) 0x8d7b7bf6), unchecked((int) 0x0df2f2ff), + unchecked((int) 0xbd6b6bd6), unchecked((int) 0xb16f6fde), unchecked((int) 0x54c5c591), unchecked((int) 0x50303060), unchecked((int) 0x03010102), + unchecked((int) 0xa96767ce), unchecked((int) 0x7d2b2b56), unchecked((int) 0x19fefee7), unchecked((int) 0x62d7d7b5), unchecked((int) 0xe6abab4d), + unchecked((int) 0x9a7676ec), unchecked((int) 0x45caca8f), unchecked((int) 0x9d82821f), unchecked((int) 0x40c9c989), unchecked((int) 0x877d7dfa), + unchecked((int) 0x15fafaef), unchecked((int) 0xeb5959b2), unchecked((int) 0xc947478e), unchecked((int) 0x0bf0f0fb), unchecked((int) 0xecadad41), + unchecked((int) 0x67d4d4b3), unchecked((int) 0xfda2a25f), unchecked((int) 0xeaafaf45), unchecked((int) 0xbf9c9c23), unchecked((int) 0xf7a4a453), + unchecked((int) 0x967272e4), unchecked((int) 0x5bc0c09b), unchecked((int) 0xc2b7b775), unchecked((int) 0x1cfdfde1), unchecked((int) 0xae93933d), + unchecked((int) 0x6a26264c), unchecked((int) 0x5a36366c), unchecked((int) 0x413f3f7e), unchecked((int) 0x02f7f7f5), unchecked((int) 0x4fcccc83), + unchecked((int) 0x5c343468), unchecked((int) 0xf4a5a551), unchecked((int) 0x34e5e5d1), unchecked((int) 0x08f1f1f9), unchecked((int) 0x937171e2), + unchecked((int) 0x73d8d8ab), unchecked((int) 0x53313162), unchecked((int) 0x3f15152a), unchecked((int) 0x0c040408), unchecked((int) 0x52c7c795), + unchecked((int) 0x65232346), unchecked((int) 0x5ec3c39d), unchecked((int) 0x28181830), unchecked((int) 0xa1969637), unchecked((int) 0x0f05050a), + unchecked((int) 0xb59a9a2f), unchecked((int) 0x0907070e), unchecked((int) 0x36121224), unchecked((int) 0x9b80801b), unchecked((int) 0x3de2e2df), + unchecked((int) 0x26ebebcd), unchecked((int) 0x6927274e), unchecked((int) 0xcdb2b27f), unchecked((int) 0x9f7575ea), unchecked((int) 0x1b090912), + unchecked((int) 0x9e83831d), unchecked((int) 0x742c2c58), unchecked((int) 0x2e1a1a34), unchecked((int) 0x2d1b1b36), unchecked((int) 0xb26e6edc), + unchecked((int) 0xee5a5ab4), unchecked((int) 0xfba0a05b), unchecked((int) 0xf65252a4), unchecked((int) 0x4d3b3b76), unchecked((int) 0x61d6d6b7), + unchecked((int) 0xceb3b37d), unchecked((int) 0x7b292952), unchecked((int) 0x3ee3e3dd), unchecked((int) 0x712f2f5e), unchecked((int) 0x97848413), + unchecked((int) 0xf55353a6), unchecked((int) 0x68d1d1b9), unchecked((int) 0x00000000), unchecked((int) 0x2cededc1), unchecked((int) 0x60202040), + unchecked((int) 0x1ffcfce3), unchecked((int) 0xc8b1b179), unchecked((int) 0xed5b5bb6), unchecked((int) 0xbe6a6ad4), unchecked((int) 0x46cbcb8d), + unchecked((int) 0xd9bebe67), unchecked((int) 0x4b393972), unchecked((int) 0xde4a4a94), unchecked((int) 0xd44c4c98), unchecked((int) 0xe85858b0), + unchecked((int) 0x4acfcf85), unchecked((int) 0x6bd0d0bb), unchecked((int) 0x2aefefc5), unchecked((int) 0xe5aaaa4f), unchecked((int) 0x16fbfbed), + unchecked((int) 0xc5434386), unchecked((int) 0xd74d4d9a), unchecked((int) 0x55333366), unchecked((int) 0x94858511), unchecked((int) 0xcf45458a), + unchecked((int) 0x10f9f9e9), unchecked((int) 0x06020204), unchecked((int) 0x817f7ffe), unchecked((int) 0xf05050a0), unchecked((int) 0x443c3c78), + unchecked((int) 0xba9f9f25), unchecked((int) 0xe3a8a84b), unchecked((int) 0xf35151a2), unchecked((int) 0xfea3a35d), unchecked((int) 0xc0404080), + unchecked((int) 0x8a8f8f05), unchecked((int) 0xad92923f), unchecked((int) 0xbc9d9d21), unchecked((int) 0x48383870), unchecked((int) 0x04f5f5f1), + unchecked((int) 0xdfbcbc63), unchecked((int) 0xc1b6b677), unchecked((int) 0x75dadaaf), unchecked((int) 0x63212142), unchecked((int) 0x30101020), + unchecked((int) 0x1affffe5), unchecked((int) 0x0ef3f3fd), unchecked((int) 0x6dd2d2bf), unchecked((int) 0x4ccdcd81), unchecked((int) 0x140c0c18), + unchecked((int) 0x35131326), unchecked((int) 0x2fececc3), unchecked((int) 0xe15f5fbe), unchecked((int) 0xa2979735), unchecked((int) 0xcc444488), + unchecked((int) 0x3917172e), unchecked((int) 0x57c4c493), unchecked((int) 0xf2a7a755), unchecked((int) 0x827e7efc), unchecked((int) 0x473d3d7a), + unchecked((int) 0xac6464c8), unchecked((int) 0xe75d5dba), unchecked((int) 0x2b191932), unchecked((int) 0x957373e6), unchecked((int) 0xa06060c0), + unchecked((int) 0x98818119), unchecked((int) 0xd14f4f9e), unchecked((int) 0x7fdcdca3), unchecked((int) 0x66222244), unchecked((int) 0x7e2a2a54), + unchecked((int) 0xab90903b), unchecked((int) 0x8388880b), unchecked((int) 0xca46468c), unchecked((int) 0x29eeeec7), unchecked((int) 0xd3b8b86b), + unchecked((int) 0x3c141428), unchecked((int) 0x79dedea7), unchecked((int) 0xe25e5ebc), unchecked((int) 0x1d0b0b16), unchecked((int) 0x76dbdbad), + unchecked((int) 0x3be0e0db), unchecked((int) 0x56323264), unchecked((int) 0x4e3a3a74), unchecked((int) 0x1e0a0a14), unchecked((int) 0xdb494992), + unchecked((int) 0x0a06060c), unchecked((int) 0x6c242448), unchecked((int) 0xe45c5cb8), unchecked((int) 0x5dc2c29f), unchecked((int) 0x6ed3d3bd), + unchecked((int) 0xefacac43), unchecked((int) 0xa66262c4), unchecked((int) 0xa8919139), unchecked((int) 0xa4959531), unchecked((int) 0x37e4e4d3), + unchecked((int) 0x8b7979f2), unchecked((int) 0x32e7e7d5), unchecked((int) 0x43c8c88b), unchecked((int) 0x5937376e), unchecked((int) 0xb76d6dda), + unchecked((int) 0x8c8d8d01), unchecked((int) 0x64d5d5b1), unchecked((int) 0xd24e4e9c), unchecked((int) 0xe0a9a949), unchecked((int) 0xb46c6cd8), + unchecked((int) 0xfa5656ac), unchecked((int) 0x07f4f4f3), unchecked((int) 0x25eaeacf), unchecked((int) 0xaf6565ca), unchecked((int) 0x8e7a7af4), + unchecked((int) 0xe9aeae47), unchecked((int) 0x18080810), unchecked((int) 0xd5baba6f), unchecked((int) 0x887878f0), unchecked((int) 0x6f25254a), + unchecked((int) 0x722e2e5c), unchecked((int) 0x241c1c38), unchecked((int) 0xf1a6a657), unchecked((int) 0xc7b4b473), unchecked((int) 0x51c6c697), + unchecked((int) 0x23e8e8cb), unchecked((int) 0x7cdddda1), unchecked((int) 0x9c7474e8), unchecked((int) 0x211f1f3e), unchecked((int) 0xdd4b4b96), + unchecked((int) 0xdcbdbd61), unchecked((int) 0x868b8b0d), unchecked((int) 0x858a8a0f), unchecked((int) 0x907070e0), unchecked((int) 0x423e3e7c), + unchecked((int) 0xc4b5b571), unchecked((int) 0xaa6666cc), unchecked((int) 0xd8484890), unchecked((int) 0x05030306), unchecked((int) 0x01f6f6f7), + unchecked((int) 0x120e0e1c), unchecked((int) 0xa36161c2), unchecked((int) 0x5f35356a), unchecked((int) 0xf95757ae), unchecked((int) 0xd0b9b969), + unchecked((int) 0x91868617), unchecked((int) 0x58c1c199), unchecked((int) 0x271d1d3a), unchecked((int) 0xb99e9e27), unchecked((int) 0x38e1e1d9), + unchecked((int) 0x13f8f8eb), unchecked((int) 0xb398982b), unchecked((int) 0x33111122), unchecked((int) 0xbb6969d2), unchecked((int) 0x70d9d9a9), + unchecked((int) 0x898e8e07), unchecked((int) 0xa7949433), unchecked((int) 0xb69b9b2d), unchecked((int) 0x221e1e3c), unchecked((int) 0x92878715), + unchecked((int) 0x20e9e9c9), unchecked((int) 0x49cece87), unchecked((int) 0xff5555aa), unchecked((int) 0x78282850), unchecked((int) 0x7adfdfa5), + unchecked((int) 0x8f8c8c03), unchecked((int) 0xf8a1a159), unchecked((int) 0x80898909), unchecked((int) 0x170d0d1a), unchecked((int) 0xdabfbf65), + unchecked((int) 0x31e6e6d7), unchecked((int) 0xc6424284), unchecked((int) 0xb86868d0), unchecked((int) 0xc3414182), unchecked((int) 0xb0999929), + unchecked((int) 0x772d2d5a), unchecked((int) 0x110f0f1e), unchecked((int) 0xcbb0b07b), unchecked((int) 0xfc5454a8), unchecked((int) 0xd6bbbb6d), + unchecked((int) 0x3a16162c)}; + + private static readonly int[] T1 = + { + unchecked((int) 0x6363c6a5), unchecked((int) 0x7c7cf884), unchecked((int) 0x7777ee99), unchecked((int) 0x7b7bf68d), unchecked((int) 0xf2f2ff0d), + unchecked((int) 0x6b6bd6bd), unchecked((int) 0x6f6fdeb1), unchecked((int) 0xc5c59154), unchecked((int) 0x30306050), unchecked((int) 0x01010203), + unchecked((int) 0x6767cea9), unchecked((int) 0x2b2b567d), unchecked((int) 0xfefee719), unchecked((int) 0xd7d7b562), unchecked((int) 0xabab4de6), + unchecked((int) 0x7676ec9a), unchecked((int) 0xcaca8f45), unchecked((int) 0x82821f9d), unchecked((int) 0xc9c98940), unchecked((int) 0x7d7dfa87), + unchecked((int) 0xfafaef15), unchecked((int) 0x5959b2eb), unchecked((int) 0x47478ec9), unchecked((int) 0xf0f0fb0b), unchecked((int) 0xadad41ec), + unchecked((int) 0xd4d4b367), unchecked((int) 0xa2a25ffd), unchecked((int) 0xafaf45ea), unchecked((int) 0x9c9c23bf), unchecked((int) 0xa4a453f7), + unchecked((int) 0x7272e496), unchecked((int) 0xc0c09b5b), unchecked((int) 0xb7b775c2), unchecked((int) 0xfdfde11c), unchecked((int) 0x93933dae), + unchecked((int) 0x26264c6a), unchecked((int) 0x36366c5a), unchecked((int) 0x3f3f7e41), unchecked((int) 0xf7f7f502), unchecked((int) 0xcccc834f), + unchecked((int) 0x3434685c), unchecked((int) 0xa5a551f4), unchecked((int) 0xe5e5d134), unchecked((int) 0xf1f1f908), unchecked((int) 0x7171e293), + unchecked((int) 0xd8d8ab73), unchecked((int) 0x31316253), unchecked((int) 0x15152a3f), unchecked((int) 0x0404080c), unchecked((int) 0xc7c79552), + unchecked((int) 0x23234665), unchecked((int) 0xc3c39d5e), unchecked((int) 0x18183028), unchecked((int) 0x969637a1), unchecked((int) 0x05050a0f), + unchecked((int) 0x9a9a2fb5), unchecked((int) 0x07070e09), unchecked((int) 0x12122436), unchecked((int) 0x80801b9b), unchecked((int) 0xe2e2df3d), + unchecked((int) 0xebebcd26), unchecked((int) 0x27274e69), unchecked((int) 0xb2b27fcd), unchecked((int) 0x7575ea9f), unchecked((int) 0x0909121b), + unchecked((int) 0x83831d9e), unchecked((int) 0x2c2c5874), unchecked((int) 0x1a1a342e), unchecked((int) 0x1b1b362d), unchecked((int) 0x6e6edcb2), + unchecked((int) 0x5a5ab4ee), unchecked((int) 0xa0a05bfb), unchecked((int) 0x5252a4f6), unchecked((int) 0x3b3b764d), unchecked((int) 0xd6d6b761), + unchecked((int) 0xb3b37dce), unchecked((int) 0x2929527b), unchecked((int) 0xe3e3dd3e), unchecked((int) 0x2f2f5e71), unchecked((int) 0x84841397), + unchecked((int) 0x5353a6f5), unchecked((int) 0xd1d1b968), unchecked((int) 0x00000000), unchecked((int) 0xededc12c), unchecked((int) 0x20204060), + unchecked((int) 0xfcfce31f), unchecked((int) 0xb1b179c8), unchecked((int) 0x5b5bb6ed), unchecked((int) 0x6a6ad4be), unchecked((int) 0xcbcb8d46), + unchecked((int) 0xbebe67d9), unchecked((int) 0x3939724b), unchecked((int) 0x4a4a94de), unchecked((int) 0x4c4c98d4), unchecked((int) 0x5858b0e8), + unchecked((int) 0xcfcf854a), unchecked((int) 0xd0d0bb6b), unchecked((int) 0xefefc52a), unchecked((int) 0xaaaa4fe5), unchecked((int) 0xfbfbed16), + unchecked((int) 0x434386c5), unchecked((int) 0x4d4d9ad7), unchecked((int) 0x33336655), unchecked((int) 0x85851194), unchecked((int) 0x45458acf), + unchecked((int) 0xf9f9e910), unchecked((int) 0x02020406), unchecked((int) 0x7f7ffe81), unchecked((int) 0x5050a0f0), unchecked((int) 0x3c3c7844), + unchecked((int) 0x9f9f25ba), unchecked((int) 0xa8a84be3), unchecked((int) 0x5151a2f3), unchecked((int) 0xa3a35dfe), unchecked((int) 0x404080c0), + unchecked((int) 0x8f8f058a), unchecked((int) 0x92923fad), unchecked((int) 0x9d9d21bc), unchecked((int) 0x38387048), unchecked((int) 0xf5f5f104), + unchecked((int) 0xbcbc63df), unchecked((int) 0xb6b677c1), unchecked((int) 0xdadaaf75), unchecked((int) 0x21214263), unchecked((int) 0x10102030), + unchecked((int) 0xffffe51a), unchecked((int) 0xf3f3fd0e), unchecked((int) 0xd2d2bf6d), unchecked((int) 0xcdcd814c), unchecked((int) 0x0c0c1814), + unchecked((int) 0x13132635), unchecked((int) 0xececc32f), unchecked((int) 0x5f5fbee1), unchecked((int) 0x979735a2), unchecked((int) 0x444488cc), + unchecked((int) 0x17172e39), unchecked((int) 0xc4c49357), unchecked((int) 0xa7a755f2), unchecked((int) 0x7e7efc82), unchecked((int) 0x3d3d7a47), + unchecked((int) 0x6464c8ac), unchecked((int) 0x5d5dbae7), unchecked((int) 0x1919322b), unchecked((int) 0x7373e695), unchecked((int) 0x6060c0a0), + unchecked((int) 0x81811998), unchecked((int) 0x4f4f9ed1), unchecked((int) 0xdcdca37f), unchecked((int) 0x22224466), unchecked((int) 0x2a2a547e), + unchecked((int) 0x90903bab), unchecked((int) 0x88880b83), unchecked((int) 0x46468cca), unchecked((int) 0xeeeec729), unchecked((int) 0xb8b86bd3), + unchecked((int) 0x1414283c), unchecked((int) 0xdedea779), unchecked((int) 0x5e5ebce2), unchecked((int) 0x0b0b161d), unchecked((int) 0xdbdbad76), + unchecked((int) 0xe0e0db3b), unchecked((int) 0x32326456), unchecked((int) 0x3a3a744e), unchecked((int) 0x0a0a141e), unchecked((int) 0x494992db), + unchecked((int) 0x06060c0a), unchecked((int) 0x2424486c), unchecked((int) 0x5c5cb8e4), unchecked((int) 0xc2c29f5d), unchecked((int) 0xd3d3bd6e), + unchecked((int) 0xacac43ef), unchecked((int) 0x6262c4a6), unchecked((int) 0x919139a8), unchecked((int) 0x959531a4), unchecked((int) 0xe4e4d337), + unchecked((int) 0x7979f28b), unchecked((int) 0xe7e7d532), unchecked((int) 0xc8c88b43), unchecked((int) 0x37376e59), unchecked((int) 0x6d6ddab7), + unchecked((int) 0x8d8d018c), unchecked((int) 0xd5d5b164), unchecked((int) 0x4e4e9cd2), unchecked((int) 0xa9a949e0), unchecked((int) 0x6c6cd8b4), + unchecked((int) 0x5656acfa), unchecked((int) 0xf4f4f307), unchecked((int) 0xeaeacf25), unchecked((int) 0x6565caaf), unchecked((int) 0x7a7af48e), + unchecked((int) 0xaeae47e9), unchecked((int) 0x08081018), unchecked((int) 0xbaba6fd5), unchecked((int) 0x7878f088), unchecked((int) 0x25254a6f), + unchecked((int) 0x2e2e5c72), unchecked((int) 0x1c1c3824), unchecked((int) 0xa6a657f1), unchecked((int) 0xb4b473c7), unchecked((int) 0xc6c69751), + unchecked((int) 0xe8e8cb23), unchecked((int) 0xdddda17c), unchecked((int) 0x7474e89c), unchecked((int) 0x1f1f3e21), unchecked((int) 0x4b4b96dd), + unchecked((int) 0xbdbd61dc), unchecked((int) 0x8b8b0d86), unchecked((int) 0x8a8a0f85), unchecked((int) 0x7070e090), unchecked((int) 0x3e3e7c42), + unchecked((int) 0xb5b571c4), unchecked((int) 0x6666ccaa), unchecked((int) 0x484890d8), unchecked((int) 0x03030605), unchecked((int) 0xf6f6f701), + unchecked((int) 0x0e0e1c12), unchecked((int) 0x6161c2a3), unchecked((int) 0x35356a5f), unchecked((int) 0x5757aef9), unchecked((int) 0xb9b969d0), + unchecked((int) 0x86861791), unchecked((int) 0xc1c19958), unchecked((int) 0x1d1d3a27), unchecked((int) 0x9e9e27b9), unchecked((int) 0xe1e1d938), + unchecked((int) 0xf8f8eb13), unchecked((int) 0x98982bb3), unchecked((int) 0x11112233), unchecked((int) 0x6969d2bb), unchecked((int) 0xd9d9a970), + unchecked((int) 0x8e8e0789), unchecked((int) 0x949433a7), unchecked((int) 0x9b9b2db6), unchecked((int) 0x1e1e3c22), unchecked((int) 0x87871592), + unchecked((int) 0xe9e9c920), unchecked((int) 0xcece8749), unchecked((int) 0x5555aaff), unchecked((int) 0x28285078), unchecked((int) 0xdfdfa57a), + unchecked((int) 0x8c8c038f), unchecked((int) 0xa1a159f8), unchecked((int) 0x89890980), unchecked((int) 0x0d0d1a17), unchecked((int) 0xbfbf65da), + unchecked((int) 0xe6e6d731), unchecked((int) 0x424284c6), unchecked((int) 0x6868d0b8), unchecked((int) 0x414182c3), unchecked((int) 0x999929b0), + unchecked((int) 0x2d2d5a77), unchecked((int) 0x0f0f1e11), unchecked((int) 0xb0b07bcb), unchecked((int) 0x5454a8fc), unchecked((int) 0xbbbb6dd6), + unchecked((int) 0x16162c3a)}; + + private static readonly int[] T2 = + { + unchecked((int) 0x63c6a563), unchecked((int) 0x7cf8847c), unchecked((int) 0x77ee9977), unchecked((int) 0x7bf68d7b), unchecked((int) 0xf2ff0df2), + unchecked((int) 0x6bd6bd6b), unchecked((int) 0x6fdeb16f), unchecked((int) 0xc59154c5), unchecked((int) 0x30605030), unchecked((int) 0x01020301), + unchecked((int) 0x67cea967), unchecked((int) 0x2b567d2b), unchecked((int) 0xfee719fe), unchecked((int) 0xd7b562d7), unchecked((int) 0xab4de6ab), + unchecked((int) 0x76ec9a76), unchecked((int) 0xca8f45ca), unchecked((int) 0x821f9d82), unchecked((int) 0xc98940c9), unchecked((int) 0x7dfa877d), + unchecked((int) 0xfaef15fa), unchecked((int) 0x59b2eb59), unchecked((int) 0x478ec947), unchecked((int) 0xf0fb0bf0), unchecked((int) 0xad41ecad), + unchecked((int) 0xd4b367d4), unchecked((int) 0xa25ffda2), unchecked((int) 0xaf45eaaf), unchecked((int) 0x9c23bf9c), unchecked((int) 0xa453f7a4), + unchecked((int) 0x72e49672), unchecked((int) 0xc09b5bc0), unchecked((int) 0xb775c2b7), unchecked((int) 0xfde11cfd), unchecked((int) 0x933dae93), + unchecked((int) 0x264c6a26), unchecked((int) 0x366c5a36), unchecked((int) 0x3f7e413f), unchecked((int) 0xf7f502f7), unchecked((int) 0xcc834fcc), + unchecked((int) 0x34685c34), unchecked((int) 0xa551f4a5), unchecked((int) 0xe5d134e5), unchecked((int) 0xf1f908f1), unchecked((int) 0x71e29371), + unchecked((int) 0xd8ab73d8), unchecked((int) 0x31625331), unchecked((int) 0x152a3f15), unchecked((int) 0x04080c04), unchecked((int) 0xc79552c7), + unchecked((int) 0x23466523), unchecked((int) 0xc39d5ec3), unchecked((int) 0x18302818), unchecked((int) 0x9637a196), unchecked((int) 0x050a0f05), + unchecked((int) 0x9a2fb59a), unchecked((int) 0x070e0907), unchecked((int) 0x12243612), unchecked((int) 0x801b9b80), unchecked((int) 0xe2df3de2), + unchecked((int) 0xebcd26eb), unchecked((int) 0x274e6927), unchecked((int) 0xb27fcdb2), unchecked((int) 0x75ea9f75), unchecked((int) 0x09121b09), + unchecked((int) 0x831d9e83), unchecked((int) 0x2c58742c), unchecked((int) 0x1a342e1a), unchecked((int) 0x1b362d1b), unchecked((int) 0x6edcb26e), + unchecked((int) 0x5ab4ee5a), unchecked((int) 0xa05bfba0), unchecked((int) 0x52a4f652), unchecked((int) 0x3b764d3b), unchecked((int) 0xd6b761d6), + unchecked((int) 0xb37dceb3), unchecked((int) 0x29527b29), unchecked((int) 0xe3dd3ee3), unchecked((int) 0x2f5e712f), unchecked((int) 0x84139784), + unchecked((int) 0x53a6f553), unchecked((int) 0xd1b968d1), unchecked((int) 0x00000000), unchecked((int) 0xedc12ced), unchecked((int) 0x20406020), + unchecked((int) 0xfce31ffc), unchecked((int) 0xb179c8b1), unchecked((int) 0x5bb6ed5b), unchecked((int) 0x6ad4be6a), unchecked((int) 0xcb8d46cb), + unchecked((int) 0xbe67d9be), unchecked((int) 0x39724b39), unchecked((int) 0x4a94de4a), unchecked((int) 0x4c98d44c), unchecked((int) 0x58b0e858), + unchecked((int) 0xcf854acf), unchecked((int) 0xd0bb6bd0), unchecked((int) 0xefc52aef), unchecked((int) 0xaa4fe5aa), unchecked((int) 0xfbed16fb), + unchecked((int) 0x4386c543), unchecked((int) 0x4d9ad74d), unchecked((int) 0x33665533), unchecked((int) 0x85119485), unchecked((int) 0x458acf45), + unchecked((int) 0xf9e910f9), unchecked((int) 0x02040602), unchecked((int) 0x7ffe817f), unchecked((int) 0x50a0f050), unchecked((int) 0x3c78443c), + unchecked((int) 0x9f25ba9f), unchecked((int) 0xa84be3a8), unchecked((int) 0x51a2f351), unchecked((int) 0xa35dfea3), unchecked((int) 0x4080c040), + unchecked((int) 0x8f058a8f), unchecked((int) 0x923fad92), unchecked((int) 0x9d21bc9d), unchecked((int) 0x38704838), unchecked((int) 0xf5f104f5), + unchecked((int) 0xbc63dfbc), unchecked((int) 0xb677c1b6), unchecked((int) 0xdaaf75da), unchecked((int) 0x21426321), unchecked((int) 0x10203010), + unchecked((int) 0xffe51aff), unchecked((int) 0xf3fd0ef3), unchecked((int) 0xd2bf6dd2), unchecked((int) 0xcd814ccd), unchecked((int) 0x0c18140c), + unchecked((int) 0x13263513), unchecked((int) 0xecc32fec), unchecked((int) 0x5fbee15f), unchecked((int) 0x9735a297), unchecked((int) 0x4488cc44), + unchecked((int) 0x172e3917), unchecked((int) 0xc49357c4), unchecked((int) 0xa755f2a7), unchecked((int) 0x7efc827e), unchecked((int) 0x3d7a473d), + unchecked((int) 0x64c8ac64), unchecked((int) 0x5dbae75d), unchecked((int) 0x19322b19), unchecked((int) 0x73e69573), unchecked((int) 0x60c0a060), + unchecked((int) 0x81199881), unchecked((int) 0x4f9ed14f), unchecked((int) 0xdca37fdc), unchecked((int) 0x22446622), unchecked((int) 0x2a547e2a), + unchecked((int) 0x903bab90), unchecked((int) 0x880b8388), unchecked((int) 0x468cca46), unchecked((int) 0xeec729ee), unchecked((int) 0xb86bd3b8), + unchecked((int) 0x14283c14), unchecked((int) 0xdea779de), unchecked((int) 0x5ebce25e), unchecked((int) 0x0b161d0b), unchecked((int) 0xdbad76db), + unchecked((int) 0xe0db3be0), unchecked((int) 0x32645632), unchecked((int) 0x3a744e3a), unchecked((int) 0x0a141e0a), unchecked((int) 0x4992db49), + unchecked((int) 0x060c0a06), unchecked((int) 0x24486c24), unchecked((int) 0x5cb8e45c), unchecked((int) 0xc29f5dc2), unchecked((int) 0xd3bd6ed3), + unchecked((int) 0xac43efac), unchecked((int) 0x62c4a662), unchecked((int) 0x9139a891), unchecked((int) 0x9531a495), unchecked((int) 0xe4d337e4), + unchecked((int) 0x79f28b79), unchecked((int) 0xe7d532e7), unchecked((int) 0xc88b43c8), unchecked((int) 0x376e5937), unchecked((int) 0x6ddab76d), + unchecked((int) 0x8d018c8d), unchecked((int) 0xd5b164d5), unchecked((int) 0x4e9cd24e), unchecked((int) 0xa949e0a9), unchecked((int) 0x6cd8b46c), + unchecked((int) 0x56acfa56), unchecked((int) 0xf4f307f4), unchecked((int) 0xeacf25ea), unchecked((int) 0x65caaf65), unchecked((int) 0x7af48e7a), + unchecked((int) 0xae47e9ae), unchecked((int) 0x08101808), unchecked((int) 0xba6fd5ba), unchecked((int) 0x78f08878), unchecked((int) 0x254a6f25), + unchecked((int) 0x2e5c722e), unchecked((int) 0x1c38241c), unchecked((int) 0xa657f1a6), unchecked((int) 0xb473c7b4), unchecked((int) 0xc69751c6), + unchecked((int) 0xe8cb23e8), unchecked((int) 0xdda17cdd), unchecked((int) 0x74e89c74), unchecked((int) 0x1f3e211f), unchecked((int) 0x4b96dd4b), + unchecked((int) 0xbd61dcbd), unchecked((int) 0x8b0d868b), unchecked((int) 0x8a0f858a), unchecked((int) 0x70e09070), unchecked((int) 0x3e7c423e), + unchecked((int) 0xb571c4b5), unchecked((int) 0x66ccaa66), unchecked((int) 0x4890d848), unchecked((int) 0x03060503), unchecked((int) 0xf6f701f6), + unchecked((int) 0x0e1c120e), unchecked((int) 0x61c2a361), unchecked((int) 0x356a5f35), unchecked((int) 0x57aef957), unchecked((int) 0xb969d0b9), + unchecked((int) 0x86179186), unchecked((int) 0xc19958c1), unchecked((int) 0x1d3a271d), unchecked((int) 0x9e27b99e), unchecked((int) 0xe1d938e1), + unchecked((int) 0xf8eb13f8), unchecked((int) 0x982bb398), unchecked((int) 0x11223311), unchecked((int) 0x69d2bb69), unchecked((int) 0xd9a970d9), + unchecked((int) 0x8e07898e), unchecked((int) 0x9433a794), unchecked((int) 0x9b2db69b), unchecked((int) 0x1e3c221e), unchecked((int) 0x87159287), + unchecked((int) 0xe9c920e9), unchecked((int) 0xce8749ce), unchecked((int) 0x55aaff55), unchecked((int) 0x28507828), unchecked((int) 0xdfa57adf), + unchecked((int) 0x8c038f8c), unchecked((int) 0xa159f8a1), unchecked((int) 0x89098089), unchecked((int) 0x0d1a170d), unchecked((int) 0xbf65dabf), + unchecked((int) 0xe6d731e6), unchecked((int) 0x4284c642), unchecked((int) 0x68d0b868), unchecked((int) 0x4182c341), unchecked((int) 0x9929b099), + unchecked((int) 0x2d5a772d), unchecked((int) 0x0f1e110f), unchecked((int) 0xb07bcbb0), unchecked((int) 0x54a8fc54), unchecked((int) 0xbb6dd6bb), + unchecked((int) 0x162c3a16)}; + + private static readonly int[] T3 = + { + unchecked((int) 0xc6a56363), unchecked((int) 0xf8847c7c), unchecked((int) 0xee997777), unchecked((int) 0xf68d7b7b), unchecked((int) 0xff0df2f2), + unchecked((int) 0xd6bd6b6b), unchecked((int) 0xdeb16f6f), unchecked((int) 0x9154c5c5), unchecked((int) 0x60503030), unchecked((int) 0x02030101), + unchecked((int) 0xcea96767), unchecked((int) 0x567d2b2b), unchecked((int) 0xe719fefe), unchecked((int) 0xb562d7d7), unchecked((int) 0x4de6abab), + unchecked((int) 0xec9a7676), unchecked((int) 0x8f45caca), unchecked((int) 0x1f9d8282), unchecked((int) 0x8940c9c9), unchecked((int) 0xfa877d7d), + unchecked((int) 0xef15fafa), unchecked((int) 0xb2eb5959), unchecked((int) 0x8ec94747), unchecked((int) 0xfb0bf0f0), unchecked((int) 0x41ecadad), + unchecked((int) 0xb367d4d4), unchecked((int) 0x5ffda2a2), unchecked((int) 0x45eaafaf), unchecked((int) 0x23bf9c9c), unchecked((int) 0x53f7a4a4), + unchecked((int) 0xe4967272), unchecked((int) 0x9b5bc0c0), unchecked((int) 0x75c2b7b7), unchecked((int) 0xe11cfdfd), unchecked((int) 0x3dae9393), + unchecked((int) 0x4c6a2626), unchecked((int) 0x6c5a3636), unchecked((int) 0x7e413f3f), unchecked((int) 0xf502f7f7), unchecked((int) 0x834fcccc), + unchecked((int) 0x685c3434), unchecked((int) 0x51f4a5a5), unchecked((int) 0xd134e5e5), unchecked((int) 0xf908f1f1), unchecked((int) 0xe2937171), + unchecked((int) 0xab73d8d8), unchecked((int) 0x62533131), unchecked((int) 0x2a3f1515), unchecked((int) 0x080c0404), unchecked((int) 0x9552c7c7), + unchecked((int) 0x46652323), unchecked((int) 0x9d5ec3c3), unchecked((int) 0x30281818), unchecked((int) 0x37a19696), unchecked((int) 0x0a0f0505), + unchecked((int) 0x2fb59a9a), unchecked((int) 0x0e090707), unchecked((int) 0x24361212), unchecked((int) 0x1b9b8080), unchecked((int) 0xdf3de2e2), + unchecked((int) 0xcd26ebeb), unchecked((int) 0x4e692727), unchecked((int) 0x7fcdb2b2), unchecked((int) 0xea9f7575), unchecked((int) 0x121b0909), + unchecked((int) 0x1d9e8383), unchecked((int) 0x58742c2c), unchecked((int) 0x342e1a1a), unchecked((int) 0x362d1b1b), unchecked((int) 0xdcb26e6e), + unchecked((int) 0xb4ee5a5a), unchecked((int) 0x5bfba0a0), unchecked((int) 0xa4f65252), unchecked((int) 0x764d3b3b), unchecked((int) 0xb761d6d6), + unchecked((int) 0x7dceb3b3), unchecked((int) 0x527b2929), unchecked((int) 0xdd3ee3e3), unchecked((int) 0x5e712f2f), unchecked((int) 0x13978484), + unchecked((int) 0xa6f55353), unchecked((int) 0xb968d1d1), unchecked((int) 0x00000000), unchecked((int) 0xc12ceded), unchecked((int) 0x40602020), + unchecked((int) 0xe31ffcfc), unchecked((int) 0x79c8b1b1), unchecked((int) 0xb6ed5b5b), unchecked((int) 0xd4be6a6a), unchecked((int) 0x8d46cbcb), + unchecked((int) 0x67d9bebe), unchecked((int) 0x724b3939), unchecked((int) 0x94de4a4a), unchecked((int) 0x98d44c4c), unchecked((int) 0xb0e85858), + unchecked((int) 0x854acfcf), unchecked((int) 0xbb6bd0d0), unchecked((int) 0xc52aefef), unchecked((int) 0x4fe5aaaa), unchecked((int) 0xed16fbfb), + unchecked((int) 0x86c54343), unchecked((int) 0x9ad74d4d), unchecked((int) 0x66553333), unchecked((int) 0x11948585), unchecked((int) 0x8acf4545), + unchecked((int) 0xe910f9f9), unchecked((int) 0x04060202), unchecked((int) 0xfe817f7f), unchecked((int) 0xa0f05050), unchecked((int) 0x78443c3c), + unchecked((int) 0x25ba9f9f), unchecked((int) 0x4be3a8a8), unchecked((int) 0xa2f35151), unchecked((int) 0x5dfea3a3), unchecked((int) 0x80c04040), + unchecked((int) 0x058a8f8f), unchecked((int) 0x3fad9292), unchecked((int) 0x21bc9d9d), unchecked((int) 0x70483838), unchecked((int) 0xf104f5f5), + unchecked((int) 0x63dfbcbc), unchecked((int) 0x77c1b6b6), unchecked((int) 0xaf75dada), unchecked((int) 0x42632121), unchecked((int) 0x20301010), + unchecked((int) 0xe51affff), unchecked((int) 0xfd0ef3f3), unchecked((int) 0xbf6dd2d2), unchecked((int) 0x814ccdcd), unchecked((int) 0x18140c0c), + unchecked((int) 0x26351313), unchecked((int) 0xc32fecec), unchecked((int) 0xbee15f5f), unchecked((int) 0x35a29797), unchecked((int) 0x88cc4444), + unchecked((int) 0x2e391717), unchecked((int) 0x9357c4c4), unchecked((int) 0x55f2a7a7), unchecked((int) 0xfc827e7e), unchecked((int) 0x7a473d3d), + unchecked((int) 0xc8ac6464), unchecked((int) 0xbae75d5d), unchecked((int) 0x322b1919), unchecked((int) 0xe6957373), unchecked((int) 0xc0a06060), + unchecked((int) 0x19988181), unchecked((int) 0x9ed14f4f), unchecked((int) 0xa37fdcdc), unchecked((int) 0x44662222), unchecked((int) 0x547e2a2a), + unchecked((int) 0x3bab9090), unchecked((int) 0x0b838888), unchecked((int) 0x8cca4646), unchecked((int) 0xc729eeee), unchecked((int) 0x6bd3b8b8), + unchecked((int) 0x283c1414), unchecked((int) 0xa779dede), unchecked((int) 0xbce25e5e), unchecked((int) 0x161d0b0b), unchecked((int) 0xad76dbdb), + unchecked((int) 0xdb3be0e0), unchecked((int) 0x64563232), unchecked((int) 0x744e3a3a), unchecked((int) 0x141e0a0a), unchecked((int) 0x92db4949), + unchecked((int) 0x0c0a0606), unchecked((int) 0x486c2424), unchecked((int) 0xb8e45c5c), unchecked((int) 0x9f5dc2c2), unchecked((int) 0xbd6ed3d3), + unchecked((int) 0x43efacac), unchecked((int) 0xc4a66262), unchecked((int) 0x39a89191), unchecked((int) 0x31a49595), unchecked((int) 0xd337e4e4), + unchecked((int) 0xf28b7979), unchecked((int) 0xd532e7e7), unchecked((int) 0x8b43c8c8), unchecked((int) 0x6e593737), unchecked((int) 0xdab76d6d), + unchecked((int) 0x018c8d8d), unchecked((int) 0xb164d5d5), unchecked((int) 0x9cd24e4e), unchecked((int) 0x49e0a9a9), unchecked((int) 0xd8b46c6c), + unchecked((int) 0xacfa5656), unchecked((int) 0xf307f4f4), unchecked((int) 0xcf25eaea), unchecked((int) 0xcaaf6565), unchecked((int) 0xf48e7a7a), + unchecked((int) 0x47e9aeae), unchecked((int) 0x10180808), unchecked((int) 0x6fd5baba), unchecked((int) 0xf0887878), unchecked((int) 0x4a6f2525), + unchecked((int) 0x5c722e2e), unchecked((int) 0x38241c1c), unchecked((int) 0x57f1a6a6), unchecked((int) 0x73c7b4b4), unchecked((int) 0x9751c6c6), + unchecked((int) 0xcb23e8e8), unchecked((int) 0xa17cdddd), unchecked((int) 0xe89c7474), unchecked((int) 0x3e211f1f), unchecked((int) 0x96dd4b4b), + unchecked((int) 0x61dcbdbd), unchecked((int) 0x0d868b8b), unchecked((int) 0x0f858a8a), unchecked((int) 0xe0907070), unchecked((int) 0x7c423e3e), + unchecked((int) 0x71c4b5b5), unchecked((int) 0xccaa6666), unchecked((int) 0x90d84848), unchecked((int) 0x06050303), unchecked((int) 0xf701f6f6), + unchecked((int) 0x1c120e0e), unchecked((int) 0xc2a36161), unchecked((int) 0x6a5f3535), unchecked((int) 0xaef95757), unchecked((int) 0x69d0b9b9), + unchecked((int) 0x17918686), unchecked((int) 0x9958c1c1), unchecked((int) 0x3a271d1d), unchecked((int) 0x27b99e9e), unchecked((int) 0xd938e1e1), + unchecked((int) 0xeb13f8f8), unchecked((int) 0x2bb39898), unchecked((int) 0x22331111), unchecked((int) 0xd2bb6969), unchecked((int) 0xa970d9d9), + unchecked((int) 0x07898e8e), unchecked((int) 0x33a79494), unchecked((int) 0x2db69b9b), unchecked((int) 0x3c221e1e), unchecked((int) 0x15928787), + unchecked((int) 0xc920e9e9), unchecked((int) 0x8749cece), unchecked((int) 0xaaff5555), unchecked((int) 0x50782828), unchecked((int) 0xa57adfdf), + unchecked((int) 0x038f8c8c), unchecked((int) 0x59f8a1a1), unchecked((int) 0x09808989), unchecked((int) 0x1a170d0d), unchecked((int) 0x65dabfbf), + unchecked((int) 0xd731e6e6), unchecked((int) 0x84c64242), unchecked((int) 0xd0b86868), unchecked((int) 0x82c34141), unchecked((int) 0x29b09999), + unchecked((int) 0x5a772d2d), unchecked((int) 0x1e110f0f), unchecked((int) 0x7bcbb0b0), unchecked((int) 0xa8fc5454), unchecked((int) 0x6dd6bbbb), + unchecked((int) 0x2c3a1616)}; + + private static readonly int[] Tinv0 = + { + unchecked((int) 0x50a7f451), unchecked((int) 0x5365417e), unchecked((int) 0xc3a4171a), unchecked((int) 0x965e273a), unchecked((int) 0xcb6bab3b), + unchecked((int) 0xf1459d1f), unchecked((int) 0xab58faac), unchecked((int) 0x9303e34b), unchecked((int) 0x55fa3020), unchecked((int) 0xf66d76ad), + unchecked((int) 0x9176cc88), unchecked((int) 0x254c02f5), unchecked((int) 0xfcd7e54f), unchecked((int) 0xd7cb2ac5), unchecked((int) 0x80443526), + unchecked((int) 0x8fa362b5), unchecked((int) 0x495ab1de), unchecked((int) 0x671bba25), unchecked((int) 0x980eea45), unchecked((int) 0xe1c0fe5d), + unchecked((int) 0x02752fc3), unchecked((int) 0x12f04c81), unchecked((int) 0xa397468d), unchecked((int) 0xc6f9d36b), unchecked((int) 0xe75f8f03), + unchecked((int) 0x959c9215), unchecked((int) 0xeb7a6dbf), unchecked((int) 0xda595295), unchecked((int) 0x2d83bed4), unchecked((int) 0xd3217458), + unchecked((int) 0x2969e049), unchecked((int) 0x44c8c98e), unchecked((int) 0x6a89c275), unchecked((int) 0x78798ef4), unchecked((int) 0x6b3e5899), + unchecked((int) 0xdd71b927), unchecked((int) 0xb64fe1be), unchecked((int) 0x17ad88f0), unchecked((int) 0x66ac20c9), unchecked((int) 0xb43ace7d), + unchecked((int) 0x184adf63), unchecked((int) 0x82311ae5), unchecked((int) 0x60335197), unchecked((int) 0x457f5362), unchecked((int) 0xe07764b1), + unchecked((int) 0x84ae6bbb), unchecked((int) 0x1ca081fe), unchecked((int) 0x942b08f9), unchecked((int) 0x58684870), unchecked((int) 0x19fd458f), + unchecked((int) 0x876cde94), unchecked((int) 0xb7f87b52), unchecked((int) 0x23d373ab), unchecked((int) 0xe2024b72), unchecked((int) 0x578f1fe3), + unchecked((int) 0x2aab5566), unchecked((int) 0x0728ebb2), unchecked((int) 0x03c2b52f), unchecked((int) 0x9a7bc586), unchecked((int) 0xa50837d3), + unchecked((int) 0xf2872830), unchecked((int) 0xb2a5bf23), unchecked((int) 0xba6a0302), unchecked((int) 0x5c8216ed), unchecked((int) 0x2b1ccf8a), + unchecked((int) 0x92b479a7), unchecked((int) 0xf0f207f3), unchecked((int) 0xa1e2694e), unchecked((int) 0xcdf4da65), unchecked((int) 0xd5be0506), + unchecked((int) 0x1f6234d1), unchecked((int) 0x8afea6c4), unchecked((int) 0x9d532e34), unchecked((int) 0xa055f3a2), unchecked((int) 0x32e18a05), + unchecked((int) 0x75ebf6a4), unchecked((int) 0x39ec830b), unchecked((int) 0xaaef6040), unchecked((int) 0x069f715e), unchecked((int) 0x51106ebd), + unchecked((int) 0xf98a213e), unchecked((int) 0x3d06dd96), unchecked((int) 0xae053edd), unchecked((int) 0x46bde64d), unchecked((int) 0xb58d5491), + unchecked((int) 0x055dc471), unchecked((int) 0x6fd40604), unchecked((int) 0xff155060), unchecked((int) 0x24fb9819), unchecked((int) 0x97e9bdd6), + unchecked((int) 0xcc434089), unchecked((int) 0x779ed967), unchecked((int) 0xbd42e8b0), unchecked((int) 0x888b8907), unchecked((int) 0x385b19e7), + unchecked((int) 0xdbeec879), unchecked((int) 0x470a7ca1), unchecked((int) 0xe90f427c), unchecked((int) 0xc91e84f8), unchecked((int) 0x00000000), + unchecked((int) 0x83868009), unchecked((int) 0x48ed2b32), unchecked((int) 0xac70111e), unchecked((int) 0x4e725a6c), unchecked((int) 0xfbff0efd), + unchecked((int) 0x5638850f), unchecked((int) 0x1ed5ae3d), unchecked((int) 0x27392d36), unchecked((int) 0x64d90f0a), unchecked((int) 0x21a65c68), + unchecked((int) 0xd1545b9b), unchecked((int) 0x3a2e3624), unchecked((int) 0xb1670a0c), unchecked((int) 0x0fe75793), unchecked((int) 0xd296eeb4), + unchecked((int) 0x9e919b1b), unchecked((int) 0x4fc5c080), unchecked((int) 0xa220dc61), unchecked((int) 0x694b775a), unchecked((int) 0x161a121c), + unchecked((int) 0x0aba93e2), unchecked((int) 0xe52aa0c0), unchecked((int) 0x43e0223c), unchecked((int) 0x1d171b12), unchecked((int) 0x0b0d090e), + unchecked((int) 0xadc78bf2), unchecked((int) 0xb9a8b62d), unchecked((int) 0xc8a91e14), unchecked((int) 0x8519f157), unchecked((int) 0x4c0775af), + unchecked((int) 0xbbdd99ee), unchecked((int) 0xfd607fa3), unchecked((int) 0x9f2601f7), unchecked((int) 0xbcf5725c), unchecked((int) 0xc53b6644), + unchecked((int) 0x347efb5b), unchecked((int) 0x7629438b), unchecked((int) 0xdcc623cb), unchecked((int) 0x68fcedb6), unchecked((int) 0x63f1e4b8), + unchecked((int) 0xcadc31d7), unchecked((int) 0x10856342), unchecked((int) 0x40229713), unchecked((int) 0x2011c684), unchecked((int) 0x7d244a85), + unchecked((int) 0xf83dbbd2), unchecked((int) 0x1132f9ae), unchecked((int) 0x6da129c7), unchecked((int) 0x4b2f9e1d), unchecked((int) 0xf330b2dc), + unchecked((int) 0xec52860d), unchecked((int) 0xd0e3c177), unchecked((int) 0x6c16b32b), unchecked((int) 0x99b970a9), unchecked((int) 0xfa489411), + unchecked((int) 0x2264e947), unchecked((int) 0xc48cfca8), unchecked((int) 0x1a3ff0a0), unchecked((int) 0xd82c7d56), unchecked((int) 0xef903322), + unchecked((int) 0xc74e4987), unchecked((int) 0xc1d138d9), unchecked((int) 0xfea2ca8c), unchecked((int) 0x360bd498), unchecked((int) 0xcf81f5a6), + unchecked((int) 0x28de7aa5), unchecked((int) 0x268eb7da), unchecked((int) 0xa4bfad3f), unchecked((int) 0xe49d3a2c), unchecked((int) 0x0d927850), + unchecked((int) 0x9bcc5f6a), unchecked((int) 0x62467e54), unchecked((int) 0xc2138df6), unchecked((int) 0xe8b8d890), unchecked((int) 0x5ef7392e), + unchecked((int) 0xf5afc382), unchecked((int) 0xbe805d9f), unchecked((int) 0x7c93d069), unchecked((int) 0xa92dd56f), unchecked((int) 0xb31225cf), + unchecked((int) 0x3b99acc8), unchecked((int) 0xa77d1810), unchecked((int) 0x6e639ce8), unchecked((int) 0x7bbb3bdb), unchecked((int) 0x097826cd), + unchecked((int) 0xf418596e), unchecked((int) 0x01b79aec), unchecked((int) 0xa89a4f83), unchecked((int) 0x656e95e6), unchecked((int) 0x7ee6ffaa), + unchecked((int) 0x08cfbc21), unchecked((int) 0xe6e815ef), unchecked((int) 0xd99be7ba), unchecked((int) 0xce366f4a), unchecked((int) 0xd4099fea), + unchecked((int) 0xd67cb029), unchecked((int) 0xafb2a431), unchecked((int) 0x31233f2a), unchecked((int) 0x3094a5c6), unchecked((int) 0xc066a235), + unchecked((int) 0x37bc4e74), unchecked((int) 0xa6ca82fc), unchecked((int) 0xb0d090e0), unchecked((int) 0x15d8a733), unchecked((int) 0x4a9804f1), + unchecked((int) 0xf7daec41), unchecked((int) 0x0e50cd7f), unchecked((int) 0x2ff69117), unchecked((int) 0x8dd64d76), unchecked((int) 0x4db0ef43), + unchecked((int) 0x544daacc), unchecked((int) 0xdf0496e4), unchecked((int) 0xe3b5d19e), unchecked((int) 0x1b886a4c), unchecked((int) 0xb81f2cc1), + unchecked((int) 0x7f516546), unchecked((int) 0x04ea5e9d), unchecked((int) 0x5d358c01), unchecked((int) 0x737487fa), unchecked((int) 0x2e410bfb), + unchecked((int) 0x5a1d67b3), unchecked((int) 0x52d2db92), unchecked((int) 0x335610e9), unchecked((int) 0x1347d66d), unchecked((int) 0x8c61d79a), + unchecked((int) 0x7a0ca137), unchecked((int) 0x8e14f859), unchecked((int) 0x893c13eb), unchecked((int) 0xee27a9ce), unchecked((int) 0x35c961b7), + unchecked((int) 0xede51ce1), unchecked((int) 0x3cb1477a), unchecked((int) 0x59dfd29c), unchecked((int) 0x3f73f255), unchecked((int) 0x79ce1418), + unchecked((int) 0xbf37c773), unchecked((int) 0xeacdf753), unchecked((int) 0x5baafd5f), unchecked((int) 0x146f3ddf), unchecked((int) 0x86db4478), + unchecked((int) 0x81f3afca), unchecked((int) 0x3ec468b9), unchecked((int) 0x2c342438), unchecked((int) 0x5f40a3c2), unchecked((int) 0x72c31d16), + unchecked((int) 0x0c25e2bc), unchecked((int) 0x8b493c28), unchecked((int) 0x41950dff), unchecked((int) 0x7101a839), unchecked((int) 0xdeb30c08), + unchecked((int) 0x9ce4b4d8), unchecked((int) 0x90c15664), unchecked((int) 0x6184cb7b), unchecked((int) 0x70b632d5), unchecked((int) 0x745c6c48), + unchecked((int) 0x4257b8d0)}; + + private static readonly int[] Tinv1 = + { + unchecked((int) 0xa7f45150), unchecked((int) 0x65417e53), unchecked((int) 0xa4171ac3), unchecked((int) 0x5e273a96), unchecked((int) 0x6bab3bcb), + unchecked((int) 0x459d1ff1), unchecked((int) 0x58faacab), unchecked((int) 0x03e34b93), unchecked((int) 0xfa302055), unchecked((int) 0x6d76adf6), + unchecked((int) 0x76cc8891), unchecked((int) 0x4c02f525), unchecked((int) 0xd7e54ffc), unchecked((int) 0xcb2ac5d7), unchecked((int) 0x44352680), + unchecked((int) 0xa362b58f), unchecked((int) 0x5ab1de49), unchecked((int) 0x1bba2567), unchecked((int) 0x0eea4598), unchecked((int) 0xc0fe5de1), + unchecked((int) 0x752fc302), unchecked((int) 0xf04c8112), unchecked((int) 0x97468da3), unchecked((int) 0xf9d36bc6), unchecked((int) 0x5f8f03e7), + unchecked((int) 0x9c921595), unchecked((int) 0x7a6dbfeb), unchecked((int) 0x595295da), unchecked((int) 0x83bed42d), unchecked((int) 0x217458d3), + unchecked((int) 0x69e04929), unchecked((int) 0xc8c98e44), unchecked((int) 0x89c2756a), unchecked((int) 0x798ef478), unchecked((int) 0x3e58996b), + unchecked((int) 0x71b927dd), unchecked((int) 0x4fe1beb6), unchecked((int) 0xad88f017), unchecked((int) 0xac20c966), unchecked((int) 0x3ace7db4), + unchecked((int) 0x4adf6318), unchecked((int) 0x311ae582), unchecked((int) 0x33519760), unchecked((int) 0x7f536245), unchecked((int) 0x7764b1e0), + unchecked((int) 0xae6bbb84), unchecked((int) 0xa081fe1c), unchecked((int) 0x2b08f994), unchecked((int) 0x68487058), unchecked((int) 0xfd458f19), + unchecked((int) 0x6cde9487), unchecked((int) 0xf87b52b7), unchecked((int) 0xd373ab23), unchecked((int) 0x024b72e2), unchecked((int) 0x8f1fe357), + unchecked((int) 0xab55662a), unchecked((int) 0x28ebb207), unchecked((int) 0xc2b52f03), unchecked((int) 0x7bc5869a), unchecked((int) 0x0837d3a5), + unchecked((int) 0x872830f2), unchecked((int) 0xa5bf23b2), unchecked((int) 0x6a0302ba), unchecked((int) 0x8216ed5c), unchecked((int) 0x1ccf8a2b), + unchecked((int) 0xb479a792), unchecked((int) 0xf207f3f0), unchecked((int) 0xe2694ea1), unchecked((int) 0xf4da65cd), unchecked((int) 0xbe0506d5), + unchecked((int) 0x6234d11f), unchecked((int) 0xfea6c48a), unchecked((int) 0x532e349d), unchecked((int) 0x55f3a2a0), unchecked((int) 0xe18a0532), + unchecked((int) 0xebf6a475), unchecked((int) 0xec830b39), unchecked((int) 0xef6040aa), unchecked((int) 0x9f715e06), unchecked((int) 0x106ebd51), + unchecked((int) 0x8a213ef9), unchecked((int) 0x06dd963d), unchecked((int) 0x053eddae), unchecked((int) 0xbde64d46), unchecked((int) 0x8d5491b5), + unchecked((int) 0x5dc47105), unchecked((int) 0xd406046f), unchecked((int) 0x155060ff), unchecked((int) 0xfb981924), unchecked((int) 0xe9bdd697), + unchecked((int) 0x434089cc), unchecked((int) 0x9ed96777), unchecked((int) 0x42e8b0bd), unchecked((int) 0x8b890788), unchecked((int) 0x5b19e738), + unchecked((int) 0xeec879db), unchecked((int) 0x0a7ca147), unchecked((int) 0x0f427ce9), unchecked((int) 0x1e84f8c9), unchecked((int) 0x00000000), + unchecked((int) 0x86800983), unchecked((int) 0xed2b3248), unchecked((int) 0x70111eac), unchecked((int) 0x725a6c4e), unchecked((int) 0xff0efdfb), + unchecked((int) 0x38850f56), unchecked((int) 0xd5ae3d1e), unchecked((int) 0x392d3627), unchecked((int) 0xd90f0a64), unchecked((int) 0xa65c6821), + unchecked((int) 0x545b9bd1), unchecked((int) 0x2e36243a), unchecked((int) 0x670a0cb1), unchecked((int) 0xe757930f), unchecked((int) 0x96eeb4d2), + unchecked((int) 0x919b1b9e), unchecked((int) 0xc5c0804f), unchecked((int) 0x20dc61a2), unchecked((int) 0x4b775a69), unchecked((int) 0x1a121c16), + unchecked((int) 0xba93e20a), unchecked((int) 0x2aa0c0e5), unchecked((int) 0xe0223c43), unchecked((int) 0x171b121d), unchecked((int) 0x0d090e0b), + unchecked((int) 0xc78bf2ad), unchecked((int) 0xa8b62db9), unchecked((int) 0xa91e14c8), unchecked((int) 0x19f15785), unchecked((int) 0x0775af4c), + unchecked((int) 0xdd99eebb), unchecked((int) 0x607fa3fd), unchecked((int) 0x2601f79f), unchecked((int) 0xf5725cbc), unchecked((int) 0x3b6644c5), + unchecked((int) 0x7efb5b34), unchecked((int) 0x29438b76), unchecked((int) 0xc623cbdc), unchecked((int) 0xfcedb668), unchecked((int) 0xf1e4b863), + unchecked((int) 0xdc31d7ca), unchecked((int) 0x85634210), unchecked((int) 0x22971340), unchecked((int) 0x11c68420), unchecked((int) 0x244a857d), + unchecked((int) 0x3dbbd2f8), unchecked((int) 0x32f9ae11), unchecked((int) 0xa129c76d), unchecked((int) 0x2f9e1d4b), unchecked((int) 0x30b2dcf3), + unchecked((int) 0x52860dec), unchecked((int) 0xe3c177d0), unchecked((int) 0x16b32b6c), unchecked((int) 0xb970a999), unchecked((int) 0x489411fa), + unchecked((int) 0x64e94722), unchecked((int) 0x8cfca8c4), unchecked((int) 0x3ff0a01a), unchecked((int) 0x2c7d56d8), unchecked((int) 0x903322ef), + unchecked((int) 0x4e4987c7), unchecked((int) 0xd138d9c1), unchecked((int) 0xa2ca8cfe), unchecked((int) 0x0bd49836), unchecked((int) 0x81f5a6cf), + unchecked((int) 0xde7aa528), unchecked((int) 0x8eb7da26), unchecked((int) 0xbfad3fa4), unchecked((int) 0x9d3a2ce4), unchecked((int) 0x9278500d), + unchecked((int) 0xcc5f6a9b), unchecked((int) 0x467e5462), unchecked((int) 0x138df6c2), unchecked((int) 0xb8d890e8), unchecked((int) 0xf7392e5e), + unchecked((int) 0xafc382f5), unchecked((int) 0x805d9fbe), unchecked((int) 0x93d0697c), unchecked((int) 0x2dd56fa9), unchecked((int) 0x1225cfb3), + unchecked((int) 0x99acc83b), unchecked((int) 0x7d1810a7), unchecked((int) 0x639ce86e), unchecked((int) 0xbb3bdb7b), unchecked((int) 0x7826cd09), + unchecked((int) 0x18596ef4), unchecked((int) 0xb79aec01), unchecked((int) 0x9a4f83a8), unchecked((int) 0x6e95e665), unchecked((int) 0xe6ffaa7e), + unchecked((int) 0xcfbc2108), unchecked((int) 0xe815efe6), unchecked((int) 0x9be7bad9), unchecked((int) 0x366f4ace), unchecked((int) 0x099fead4), + unchecked((int) 0x7cb029d6), unchecked((int) 0xb2a431af), unchecked((int) 0x233f2a31), unchecked((int) 0x94a5c630), unchecked((int) 0x66a235c0), + unchecked((int) 0xbc4e7437), unchecked((int) 0xca82fca6), unchecked((int) 0xd090e0b0), unchecked((int) 0xd8a73315), unchecked((int) 0x9804f14a), + unchecked((int) 0xdaec41f7), unchecked((int) 0x50cd7f0e), unchecked((int) 0xf691172f), unchecked((int) 0xd64d768d), unchecked((int) 0xb0ef434d), + unchecked((int) 0x4daacc54), unchecked((int) 0x0496e4df), unchecked((int) 0xb5d19ee3), unchecked((int) 0x886a4c1b), unchecked((int) 0x1f2cc1b8), + unchecked((int) 0x5165467f), unchecked((int) 0xea5e9d04), unchecked((int) 0x358c015d), unchecked((int) 0x7487fa73), unchecked((int) 0x410bfb2e), + unchecked((int) 0x1d67b35a), unchecked((int) 0xd2db9252), unchecked((int) 0x5610e933), unchecked((int) 0x47d66d13), unchecked((int) 0x61d79a8c), + unchecked((int) 0x0ca1377a), unchecked((int) 0x14f8598e), unchecked((int) 0x3c13eb89), unchecked((int) 0x27a9ceee), unchecked((int) 0xc961b735), + unchecked((int) 0xe51ce1ed), unchecked((int) 0xb1477a3c), unchecked((int) 0xdfd29c59), unchecked((int) 0x73f2553f), unchecked((int) 0xce141879), + unchecked((int) 0x37c773bf), unchecked((int) 0xcdf753ea), unchecked((int) 0xaafd5f5b), unchecked((int) 0x6f3ddf14), unchecked((int) 0xdb447886), + unchecked((int) 0xf3afca81), unchecked((int) 0xc468b93e), unchecked((int) 0x3424382c), unchecked((int) 0x40a3c25f), unchecked((int) 0xc31d1672), + unchecked((int) 0x25e2bc0c), unchecked((int) 0x493c288b), unchecked((int) 0x950dff41), unchecked((int) 0x01a83971), unchecked((int) 0xb30c08de), + unchecked((int) 0xe4b4d89c), unchecked((int) 0xc1566490), unchecked((int) 0x84cb7b61), unchecked((int) 0xb632d570), unchecked((int) 0x5c6c4874), + unchecked((int) 0x57b8d042)}; + + private static readonly int[] Tinv2 = + { + unchecked((int) 0xf45150a7), unchecked((int) 0x417e5365), unchecked((int) 0x171ac3a4), unchecked((int) 0x273a965e), unchecked((int) 0xab3bcb6b), + unchecked((int) 0x9d1ff145), unchecked((int) 0xfaacab58), unchecked((int) 0xe34b9303), unchecked((int) 0x302055fa), unchecked((int) 0x76adf66d), + unchecked((int) 0xcc889176), unchecked((int) 0x02f5254c), unchecked((int) 0xe54ffcd7), unchecked((int) 0x2ac5d7cb), unchecked((int) 0x35268044), + unchecked((int) 0x62b58fa3), unchecked((int) 0xb1de495a), unchecked((int) 0xba25671b), unchecked((int) 0xea45980e), unchecked((int) 0xfe5de1c0), + unchecked((int) 0x2fc30275), unchecked((int) 0x4c8112f0), unchecked((int) 0x468da397), unchecked((int) 0xd36bc6f9), unchecked((int) 0x8f03e75f), + unchecked((int) 0x9215959c), unchecked((int) 0x6dbfeb7a), unchecked((int) 0x5295da59), unchecked((int) 0xbed42d83), unchecked((int) 0x7458d321), + unchecked((int) 0xe0492969), unchecked((int) 0xc98e44c8), unchecked((int) 0xc2756a89), unchecked((int) 0x8ef47879), unchecked((int) 0x58996b3e), + unchecked((int) 0xb927dd71), unchecked((int) 0xe1beb64f), unchecked((int) 0x88f017ad), unchecked((int) 0x20c966ac), unchecked((int) 0xce7db43a), + unchecked((int) 0xdf63184a), unchecked((int) 0x1ae58231), unchecked((int) 0x51976033), unchecked((int) 0x5362457f), unchecked((int) 0x64b1e077), + unchecked((int) 0x6bbb84ae), unchecked((int) 0x81fe1ca0), unchecked((int) 0x08f9942b), unchecked((int) 0x48705868), unchecked((int) 0x458f19fd), + unchecked((int) 0xde94876c), unchecked((int) 0x7b52b7f8), unchecked((int) 0x73ab23d3), unchecked((int) 0x4b72e202), unchecked((int) 0x1fe3578f), + unchecked((int) 0x55662aab), unchecked((int) 0xebb20728), unchecked((int) 0xb52f03c2), unchecked((int) 0xc5869a7b), unchecked((int) 0x37d3a508), + unchecked((int) 0x2830f287), unchecked((int) 0xbf23b2a5), unchecked((int) 0x0302ba6a), unchecked((int) 0x16ed5c82), unchecked((int) 0xcf8a2b1c), + unchecked((int) 0x79a792b4), unchecked((int) 0x07f3f0f2), unchecked((int) 0x694ea1e2), unchecked((int) 0xda65cdf4), unchecked((int) 0x0506d5be), + unchecked((int) 0x34d11f62), unchecked((int) 0xa6c48afe), unchecked((int) 0x2e349d53), unchecked((int) 0xf3a2a055), unchecked((int) 0x8a0532e1), + unchecked((int) 0xf6a475eb), unchecked((int) 0x830b39ec), unchecked((int) 0x6040aaef), unchecked((int) 0x715e069f), unchecked((int) 0x6ebd5110), + unchecked((int) 0x213ef98a), unchecked((int) 0xdd963d06), unchecked((int) 0x3eddae05), unchecked((int) 0xe64d46bd), unchecked((int) 0x5491b58d), + unchecked((int) 0xc471055d), unchecked((int) 0x06046fd4), unchecked((int) 0x5060ff15), unchecked((int) 0x981924fb), unchecked((int) 0xbdd697e9), + unchecked((int) 0x4089cc43), unchecked((int) 0xd967779e), unchecked((int) 0xe8b0bd42), unchecked((int) 0x8907888b), unchecked((int) 0x19e7385b), + unchecked((int) 0xc879dbee), unchecked((int) 0x7ca1470a), unchecked((int) 0x427ce90f), unchecked((int) 0x84f8c91e), unchecked((int) 0x00000000), + unchecked((int) 0x80098386), unchecked((int) 0x2b3248ed), unchecked((int) 0x111eac70), unchecked((int) 0x5a6c4e72), unchecked((int) 0x0efdfbff), + unchecked((int) 0x850f5638), unchecked((int) 0xae3d1ed5), unchecked((int) 0x2d362739), unchecked((int) 0x0f0a64d9), unchecked((int) 0x5c6821a6), + unchecked((int) 0x5b9bd154), unchecked((int) 0x36243a2e), unchecked((int) 0x0a0cb167), unchecked((int) 0x57930fe7), unchecked((int) 0xeeb4d296), + unchecked((int) 0x9b1b9e91), unchecked((int) 0xc0804fc5), unchecked((int) 0xdc61a220), unchecked((int) 0x775a694b), unchecked((int) 0x121c161a), + unchecked((int) 0x93e20aba), unchecked((int) 0xa0c0e52a), unchecked((int) 0x223c43e0), unchecked((int) 0x1b121d17), unchecked((int) 0x090e0b0d), + unchecked((int) 0x8bf2adc7), unchecked((int) 0xb62db9a8), unchecked((int) 0x1e14c8a9), unchecked((int) 0xf1578519), unchecked((int) 0x75af4c07), + unchecked((int) 0x99eebbdd), unchecked((int) 0x7fa3fd60), unchecked((int) 0x01f79f26), unchecked((int) 0x725cbcf5), unchecked((int) 0x6644c53b), + unchecked((int) 0xfb5b347e), unchecked((int) 0x438b7629), unchecked((int) 0x23cbdcc6), unchecked((int) 0xedb668fc), unchecked((int) 0xe4b863f1), + unchecked((int) 0x31d7cadc), unchecked((int) 0x63421085), unchecked((int) 0x97134022), unchecked((int) 0xc6842011), unchecked((int) 0x4a857d24), + unchecked((int) 0xbbd2f83d), unchecked((int) 0xf9ae1132), unchecked((int) 0x29c76da1), unchecked((int) 0x9e1d4b2f), unchecked((int) 0xb2dcf330), + unchecked((int) 0x860dec52), unchecked((int) 0xc177d0e3), unchecked((int) 0xb32b6c16), unchecked((int) 0x70a999b9), unchecked((int) 0x9411fa48), + unchecked((int) 0xe9472264), unchecked((int) 0xfca8c48c), unchecked((int) 0xf0a01a3f), unchecked((int) 0x7d56d82c), unchecked((int) 0x3322ef90), + unchecked((int) 0x4987c74e), unchecked((int) 0x38d9c1d1), unchecked((int) 0xca8cfea2), unchecked((int) 0xd498360b), unchecked((int) 0xf5a6cf81), + unchecked((int) 0x7aa528de), unchecked((int) 0xb7da268e), unchecked((int) 0xad3fa4bf), unchecked((int) 0x3a2ce49d), unchecked((int) 0x78500d92), + unchecked((int) 0x5f6a9bcc), unchecked((int) 0x7e546246), unchecked((int) 0x8df6c213), unchecked((int) 0xd890e8b8), unchecked((int) 0x392e5ef7), + unchecked((int) 0xc382f5af), unchecked((int) 0x5d9fbe80), unchecked((int) 0xd0697c93), unchecked((int) 0xd56fa92d), unchecked((int) 0x25cfb312), + unchecked((int) 0xacc83b99), unchecked((int) 0x1810a77d), unchecked((int) 0x9ce86e63), unchecked((int) 0x3bdb7bbb), unchecked((int) 0x26cd0978), + unchecked((int) 0x596ef418), unchecked((int) 0x9aec01b7), unchecked((int) 0x4f83a89a), unchecked((int) 0x95e6656e), unchecked((int) 0xffaa7ee6), + unchecked((int) 0xbc2108cf), unchecked((int) 0x15efe6e8), unchecked((int) 0xe7bad99b), unchecked((int) 0x6f4ace36), unchecked((int) 0x9fead409), + unchecked((int) 0xb029d67c), unchecked((int) 0xa431afb2), unchecked((int) 0x3f2a3123), unchecked((int) 0xa5c63094), unchecked((int) 0xa235c066), + unchecked((int) 0x4e7437bc), unchecked((int) 0x82fca6ca), unchecked((int) 0x90e0b0d0), unchecked((int) 0xa73315d8), unchecked((int) 0x04f14a98), + unchecked((int) 0xec41f7da), unchecked((int) 0xcd7f0e50), unchecked((int) 0x91172ff6), unchecked((int) 0x4d768dd6), unchecked((int) 0xef434db0), + unchecked((int) 0xaacc544d), unchecked((int) 0x96e4df04), unchecked((int) 0xd19ee3b5), unchecked((int) 0x6a4c1b88), unchecked((int) 0x2cc1b81f), + unchecked((int) 0x65467f51), unchecked((int) 0x5e9d04ea), unchecked((int) 0x8c015d35), unchecked((int) 0x87fa7374), unchecked((int) 0x0bfb2e41), + unchecked((int) 0x67b35a1d), unchecked((int) 0xdb9252d2), unchecked((int) 0x10e93356), unchecked((int) 0xd66d1347), unchecked((int) 0xd79a8c61), + unchecked((int) 0xa1377a0c), unchecked((int) 0xf8598e14), unchecked((int) 0x13eb893c), unchecked((int) 0xa9ceee27), unchecked((int) 0x61b735c9), + unchecked((int) 0x1ce1ede5), unchecked((int) 0x477a3cb1), unchecked((int) 0xd29c59df), unchecked((int) 0xf2553f73), unchecked((int) 0x141879ce), + unchecked((int) 0xc773bf37), unchecked((int) 0xf753eacd), unchecked((int) 0xfd5f5baa), unchecked((int) 0x3ddf146f), unchecked((int) 0x447886db), + unchecked((int) 0xafca81f3), unchecked((int) 0x68b93ec4), unchecked((int) 0x24382c34), unchecked((int) 0xa3c25f40), unchecked((int) 0x1d1672c3), + unchecked((int) 0xe2bc0c25), unchecked((int) 0x3c288b49), unchecked((int) 0x0dff4195), unchecked((int) 0xa8397101), unchecked((int) 0x0c08deb3), + unchecked((int) 0xb4d89ce4), unchecked((int) 0x566490c1), unchecked((int) 0xcb7b6184), unchecked((int) 0x32d570b6), unchecked((int) 0x6c48745c), + unchecked((int) 0xb8d04257)}; + + private static readonly int[] Tinv3 = + { + unchecked((int) 0x5150a7f4), unchecked((int) 0x7e536541), unchecked((int) 0x1ac3a417), unchecked((int) 0x3a965e27), unchecked((int) 0x3bcb6bab), + unchecked((int) 0x1ff1459d), unchecked((int) 0xacab58fa), unchecked((int) 0x4b9303e3), unchecked((int) 0x2055fa30), unchecked((int) 0xadf66d76), + unchecked((int) 0x889176cc), unchecked((int) 0xf5254c02), unchecked((int) 0x4ffcd7e5), unchecked((int) 0xc5d7cb2a), unchecked((int) 0x26804435), + unchecked((int) 0xb58fa362), unchecked((int) 0xde495ab1), unchecked((int) 0x25671bba), unchecked((int) 0x45980eea), unchecked((int) 0x5de1c0fe), + unchecked((int) 0xc302752f), unchecked((int) 0x8112f04c), unchecked((int) 0x8da39746), unchecked((int) 0x6bc6f9d3), unchecked((int) 0x03e75f8f), + unchecked((int) 0x15959c92), unchecked((int) 0xbfeb7a6d), unchecked((int) 0x95da5952), unchecked((int) 0xd42d83be), unchecked((int) 0x58d32174), + unchecked((int) 0x492969e0), unchecked((int) 0x8e44c8c9), unchecked((int) 0x756a89c2), unchecked((int) 0xf478798e), unchecked((int) 0x996b3e58), + unchecked((int) 0x27dd71b9), unchecked((int) 0xbeb64fe1), unchecked((int) 0xf017ad88), unchecked((int) 0xc966ac20), unchecked((int) 0x7db43ace), + unchecked((int) 0x63184adf), unchecked((int) 0xe582311a), unchecked((int) 0x97603351), unchecked((int) 0x62457f53), unchecked((int) 0xb1e07764), + unchecked((int) 0xbb84ae6b), unchecked((int) 0xfe1ca081), unchecked((int) 0xf9942b08), unchecked((int) 0x70586848), unchecked((int) 0x8f19fd45), + unchecked((int) 0x94876cde), unchecked((int) 0x52b7f87b), unchecked((int) 0xab23d373), unchecked((int) 0x72e2024b), unchecked((int) 0xe3578f1f), + unchecked((int) 0x662aab55), unchecked((int) 0xb20728eb), unchecked((int) 0x2f03c2b5), unchecked((int) 0x869a7bc5), unchecked((int) 0xd3a50837), + unchecked((int) 0x30f28728), unchecked((int) 0x23b2a5bf), unchecked((int) 0x02ba6a03), unchecked((int) 0xed5c8216), unchecked((int) 0x8a2b1ccf), + unchecked((int) 0xa792b479), unchecked((int) 0xf3f0f207), unchecked((int) 0x4ea1e269), unchecked((int) 0x65cdf4da), unchecked((int) 0x06d5be05), + unchecked((int) 0xd11f6234), unchecked((int) 0xc48afea6), unchecked((int) 0x349d532e), unchecked((int) 0xa2a055f3), unchecked((int) 0x0532e18a), + unchecked((int) 0xa475ebf6), unchecked((int) 0x0b39ec83), unchecked((int) 0x40aaef60), unchecked((int) 0x5e069f71), unchecked((int) 0xbd51106e), + unchecked((int) 0x3ef98a21), unchecked((int) 0x963d06dd), unchecked((int) 0xddae053e), unchecked((int) 0x4d46bde6), unchecked((int) 0x91b58d54), + unchecked((int) 0x71055dc4), unchecked((int) 0x046fd406), unchecked((int) 0x60ff1550), unchecked((int) 0x1924fb98), unchecked((int) 0xd697e9bd), + unchecked((int) 0x89cc4340), unchecked((int) 0x67779ed9), unchecked((int) 0xb0bd42e8), unchecked((int) 0x07888b89), unchecked((int) 0xe7385b19), + unchecked((int) 0x79dbeec8), unchecked((int) 0xa1470a7c), unchecked((int) 0x7ce90f42), unchecked((int) 0xf8c91e84), unchecked((int) 0x00000000), + unchecked((int) 0x09838680), unchecked((int) 0x3248ed2b), unchecked((int) 0x1eac7011), unchecked((int) 0x6c4e725a), unchecked((int) 0xfdfbff0e), + unchecked((int) 0x0f563885), unchecked((int) 0x3d1ed5ae), unchecked((int) 0x3627392d), unchecked((int) 0x0a64d90f), unchecked((int) 0x6821a65c), + unchecked((int) 0x9bd1545b), unchecked((int) 0x243a2e36), unchecked((int) 0x0cb1670a), unchecked((int) 0x930fe757), unchecked((int) 0xb4d296ee), + unchecked((int) 0x1b9e919b), unchecked((int) 0x804fc5c0), unchecked((int) 0x61a220dc), unchecked((int) 0x5a694b77), unchecked((int) 0x1c161a12), + unchecked((int) 0xe20aba93), unchecked((int) 0xc0e52aa0), unchecked((int) 0x3c43e022), unchecked((int) 0x121d171b), unchecked((int) 0x0e0b0d09), + unchecked((int) 0xf2adc78b), unchecked((int) 0x2db9a8b6), unchecked((int) 0x14c8a91e), unchecked((int) 0x578519f1), unchecked((int) 0xaf4c0775), + unchecked((int) 0xeebbdd99), unchecked((int) 0xa3fd607f), unchecked((int) 0xf79f2601), unchecked((int) 0x5cbcf572), unchecked((int) 0x44c53b66), + unchecked((int) 0x5b347efb), unchecked((int) 0x8b762943), unchecked((int) 0xcbdcc623), unchecked((int) 0xb668fced), unchecked((int) 0xb863f1e4), + unchecked((int) 0xd7cadc31), unchecked((int) 0x42108563), unchecked((int) 0x13402297), unchecked((int) 0x842011c6), unchecked((int) 0x857d244a), + unchecked((int) 0xd2f83dbb), unchecked((int) 0xae1132f9), unchecked((int) 0xc76da129), unchecked((int) 0x1d4b2f9e), unchecked((int) 0xdcf330b2), + unchecked((int) 0x0dec5286), unchecked((int) 0x77d0e3c1), unchecked((int) 0x2b6c16b3), unchecked((int) 0xa999b970), unchecked((int) 0x11fa4894), + unchecked((int) 0x472264e9), unchecked((int) 0xa8c48cfc), unchecked((int) 0xa01a3ff0), unchecked((int) 0x56d82c7d), unchecked((int) 0x22ef9033), + unchecked((int) 0x87c74e49), unchecked((int) 0xd9c1d138), unchecked((int) 0x8cfea2ca), unchecked((int) 0x98360bd4), unchecked((int) 0xa6cf81f5), + unchecked((int) 0xa528de7a), unchecked((int) 0xda268eb7), unchecked((int) 0x3fa4bfad), unchecked((int) 0x2ce49d3a), unchecked((int) 0x500d9278), + unchecked((int) 0x6a9bcc5f), unchecked((int) 0x5462467e), unchecked((int) 0xf6c2138d), unchecked((int) 0x90e8b8d8), unchecked((int) 0x2e5ef739), + unchecked((int) 0x82f5afc3), unchecked((int) 0x9fbe805d), unchecked((int) 0x697c93d0), unchecked((int) 0x6fa92dd5), unchecked((int) 0xcfb31225), + unchecked((int) 0xc83b99ac), unchecked((int) 0x10a77d18), unchecked((int) 0xe86e639c), unchecked((int) 0xdb7bbb3b), unchecked((int) 0xcd097826), + unchecked((int) 0x6ef41859), unchecked((int) 0xec01b79a), unchecked((int) 0x83a89a4f), unchecked((int) 0xe6656e95), unchecked((int) 0xaa7ee6ff), + unchecked((int) 0x2108cfbc), unchecked((int) 0xefe6e815), unchecked((int) 0xbad99be7), unchecked((int) 0x4ace366f), unchecked((int) 0xead4099f), + unchecked((int) 0x29d67cb0), unchecked((int) 0x31afb2a4), unchecked((int) 0x2a31233f), unchecked((int) 0xc63094a5), unchecked((int) 0x35c066a2), + unchecked((int) 0x7437bc4e), unchecked((int) 0xfca6ca82), unchecked((int) 0xe0b0d090), unchecked((int) 0x3315d8a7), unchecked((int) 0xf14a9804), + unchecked((int) 0x41f7daec), unchecked((int) 0x7f0e50cd), unchecked((int) 0x172ff691), unchecked((int) 0x768dd64d), unchecked((int) 0x434db0ef), + unchecked((int) 0xcc544daa), unchecked((int) 0xe4df0496), unchecked((int) 0x9ee3b5d1), unchecked((int) 0x4c1b886a), unchecked((int) 0xc1b81f2c), + unchecked((int) 0x467f5165), unchecked((int) 0x9d04ea5e), unchecked((int) 0x015d358c), unchecked((int) 0xfa737487), unchecked((int) 0xfb2e410b), + unchecked((int) 0xb35a1d67), unchecked((int) 0x9252d2db), unchecked((int) 0xe9335610), unchecked((int) 0x6d1347d6), unchecked((int) 0x9a8c61d7), + unchecked((int) 0x377a0ca1), unchecked((int) 0x598e14f8), unchecked((int) 0xeb893c13), unchecked((int) 0xceee27a9), unchecked((int) 0xb735c961), + unchecked((int) 0xe1ede51c), unchecked((int) 0x7a3cb147), unchecked((int) 0x9c59dfd2), unchecked((int) 0x553f73f2), unchecked((int) 0x1879ce14), + unchecked((int) 0x73bf37c7), unchecked((int) 0x53eacdf7), unchecked((int) 0x5f5baafd), unchecked((int) 0xdf146f3d), unchecked((int) 0x7886db44), + unchecked((int) 0xca81f3af), unchecked((int) 0xb93ec468), unchecked((int) 0x382c3424), unchecked((int) 0xc25f40a3), unchecked((int) 0x1672c31d), + unchecked((int) 0xbc0c25e2), unchecked((int) 0x288b493c), unchecked((int) 0xff41950d), unchecked((int) 0x397101a8), unchecked((int) 0x08deb30c), + unchecked((int) 0xd89ce4b4), unchecked((int) 0x6490c156), unchecked((int) 0x7b6184cb), unchecked((int) 0xd570b632), unchecked((int) 0x48745c6c), + unchecked((int) 0xd04257b8)}; + + private int Shift( + int r, + int shift) + { + return ((int) ( ( (uint) r >> shift) | + (uint) (r << (32 - shift)) + )); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const int m1 = unchecked((int) 0x80808080); + private const int m2 = unchecked((int) 0x7f7f7f7f); + private const int m3 = unchecked((int) 0x0000001b); + private int FFmulX(int x) { + return ( (int) ( ((x & m2) << 1) ^ + (( (uint)(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 int Inv_Mcol(int x) { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + + private int SubWord(int x) { + return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | 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 int[,] 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 + int[,] W = new int[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] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + int 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 int[,] WorkingKey; + private int 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"); + } + + 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) + { + int index = off; + + C0 = (bytes[index++] & 0xff); + C0 |= (bytes[index++] & 0xff) << 8; + C0 |= (bytes[index++] & 0xff) << 16; + C0 |= bytes[index++] << 24; + + C1 = (bytes[index++] & 0xff); + C1 |= (bytes[index++] & 0xff) << 8; + C1 |= (bytes[index++] & 0xff) << 16; + C1 |= bytes[index++] << 24; + + C2 = (bytes[index++] & 0xff); + C2 |= (bytes[index++] & 0xff) << 8; + C2 |= (bytes[index++] & 0xff) << 16; + C2 |= bytes[index++] << 24; + + C3 = (bytes[index++] & 0xff); + C3 |= (bytes[index++] & 0xff) << 8; + C3 |= (bytes[index++] & 0xff) << 16; + C3 |= bytes[index++] << 24; + } + + private void PackBlock( + byte[] bytes, + int off) + { + int index = off; + + bytes[index++] = (byte)C0; + bytes[index++] = (byte)(C0 >> 8); + bytes[index++] = (byte)(C0 >> 16); + bytes[index++] = (byte)(C0 >> 24); + + bytes[index++] = (byte)C1; + bytes[index++] = (byte)(C1 >> 8); + bytes[index++] = (byte)(C1 >> 16); + bytes[index++] = (byte)(C1 >> 24); + + bytes[index++] = (byte)C2; + bytes[index++] = (byte)(C2 >> 8); + bytes[index++] = (byte)(C2 >> 16); + bytes[index++] = (byte)(C2 >> 24); + + bytes[index++] = (byte)C3; + bytes[index++] = (byte)(C3 >> 8); + bytes[index++] = (byte)(C3 >> 16); + bytes[index++] = (byte)(C3 >> 24); + } + + private void EncryptBlock(int[,] KW) + { + int 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] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[(C3>>24)&255] ^ KW[r,0]; + r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[(C0>>24)&255] ^ KW[r,1]; + r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[(C1>>24)&255] ^ KW[r,2]; + r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[(C2>>24)&255] ^ KW[r++,3]; + C0 = T0[r0&255] ^ T1[(r1>>8)&255] ^ T2[(r2>>16)&255] ^ T3[(r3>>24)&255] ^ KW[r,0]; + C1 = T0[r1&255] ^ T1[(r2>>8)&255] ^ T2[(r3>>16)&255] ^ T3[(r0>>24)&255] ^ KW[r,1]; + C2 = T0[r2&255] ^ T1[(r3>>8)&255] ^ T2[(r0>>16)&255] ^ T3[(r1>>24)&255] ^ KW[r,2]; + C3 = T0[r3&255] ^ T1[(r0>>8)&255] ^ T2[(r1>>16)&255] ^ T3[(r2>>24)&255] ^ KW[r++,3]; + } + + r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[(C3>>24)&255] ^ KW[r,0]; + r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[(C0>>24)&255] ^ KW[r,1]; + r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[(C1>>24)&255] ^ KW[r,2]; + r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[(C2>>24)&255] ^ 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 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r,0]; + C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r,1]; + C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r,2]; + C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r,3]; + + } + + private void DecryptBlock(int[,] KW) + { + int r, 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)&255] ^ KW[r,0]; + r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[(C2>>24)&255] ^ KW[r,1]; + r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[(C3>>24)&255] ^ KW[r,2]; + r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ KW[r--,3]; + C0 = Tinv0[r0&255] ^ Tinv1[(r3>>8)&255] ^ Tinv2[(r2>>16)&255] ^ Tinv3[(r1>>24)&255] ^ KW[r,0]; + C1 = Tinv0[r1&255] ^ Tinv1[(r0>>8)&255] ^ Tinv2[(r3>>16)&255] ^ Tinv3[(r2>>24)&255] ^ KW[r,1]; + C2 = Tinv0[r2&255] ^ Tinv1[(r1>>8)&255] ^ Tinv2[(r0>>16)&255] ^ Tinv3[(r3>>24)&255] ^ KW[r,2]; + C3 = Tinv0[r3&255] ^ Tinv1[(r2>>8)&255] ^ Tinv2[(r1>>16)&255] ^ Tinv3[(r0>>24)&255] ^ KW[r--,3]; + } + + r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[(C1>>24)&255] ^ KW[r,0]; + r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[(C2>>24)&255] ^ KW[r,1]; + r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[(C3>>24)&255] ^ KW[r,2]; + r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ 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 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0,0]; + C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0,1]; + C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0,2]; + C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0,3]; + } + } +} diff --git a/src/core/srcbc/crypto/engines/AesLightEngine.cs b/src/core/srcbc/crypto/engines/AesLightEngine.cs new file mode 100644 index 0000000..57ec6f7 --- /dev/null +++ b/src/core/srcbc/crypto/engines/AesLightEngine.cs @@ -0,0 +1,438 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * an implementation of the AES (Rijndael), from FIPS-197. + *+ * For further details see: http://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * 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. + *
+ *+ * This file contains the slowest performance version with no static tables + * for round precomputation, but it has the smallest foot print. + *
+ */ + public class AesLightEngine + : IBlockCipher + { + // The S box + private static readonly byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, + (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, + (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, + (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, + (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, + (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, + (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, + (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, + (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, + (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, + (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, + (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, + (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, + (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, + (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, + (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, + (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + // The inverse S-box + private static readonly byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, + (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, + (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, + (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, + (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, + (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, + (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, + (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, + (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, + (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, + (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, + (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, + (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, + (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, + (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, + (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, + (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static readonly int[] 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 int Shift( + int r, + int shift) + { + return ((int) ( ( (uint) r >> shift) | + (uint) (r << (32 - shift)) ) + ); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const int m1 = unchecked((int) 0x80808080); + private const int m2 = unchecked((int) 0x7f7f7f7f); + private const int m3 = unchecked((int) 0x0000001b); + + private int FFmulX(int x) + { + return ( (int) ( ((x & m2) << 1) ^ + (( (uint)(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 int Mcol(int x) + { + int f2 = FFmulX(x); + return f2 ^ Shift(x ^ f2, 8) ^ Shift(x, 16) ^ Shift(x, 24); + } + + private int Inv_Mcol(int x) + { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + + private int SubWord(int x) + { + return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | 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 int[,] 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 + int[,] W = new int[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] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + int 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 int[,] WorkingKey; + private int 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) + { + int index = off; + + C0 = (bytes[index++] & 0xff); + C0 |= (bytes[index++] & 0xff) << 8; + C0 |= (bytes[index++] & 0xff) << 16; + C0 |= bytes[index++] << 24; + + C1 = (bytes[index++] & 0xff); + C1 |= (bytes[index++] & 0xff) << 8; + C1 |= (bytes[index++] & 0xff) << 16; + C1 |= bytes[index++] << 24; + + C2 = (bytes[index++] & 0xff); + C2 |= (bytes[index++] & 0xff) << 8; + C2 |= (bytes[index++] & 0xff) << 16; + C2 |= bytes[index++] << 24; + + C3 = (bytes[index++] & 0xff); + C3 |= (bytes[index++] & 0xff) << 8; + C3 |= (bytes[index++] & 0xff) << 16; + C3 |= bytes[index++] << 24; + } + + private void PackBlock( + byte[] bytes, + int off) + { + int index = off; + + bytes[index++] = (byte)C0; + bytes[index++] = (byte)(C0 >> 8); + bytes[index++] = (byte)(C0 >> 16); + bytes[index++] = (byte)(C0 >> 24); + + bytes[index++] = (byte)C1; + bytes[index++] = (byte)(C1 >> 8); + bytes[index++] = (byte)(C1 >> 16); + bytes[index++] = (byte)(C1 >> 24); + + bytes[index++] = (byte)C2; + bytes[index++] = (byte)(C2 >> 8); + bytes[index++] = (byte)(C2 >> 16); + bytes[index++] = (byte)(C2 >> 24); + + bytes[index++] = (byte)C3; + bytes[index++] = (byte)(C3 >> 8); + bytes[index++] = (byte)(C3 >> 16); + bytes[index++] = (byte)(C3 >> 24); + } + + private void EncryptBlock(int[,] KW) + { + int 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 = Mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r,0]; + r1 = Mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r,1]; + r2 = Mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r,2]; + r3 = Mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++,3]; + C0 = Mcol((S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r,0]; + C1 = Mcol((S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24)) ^ KW[r,1]; + C2 = Mcol((S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24)) ^ KW[r,2]; + C3 = Mcol((S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24)) ^ KW[r++,3]; + } + + r0 = Mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r,0]; + r1 = Mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r,1]; + r2 = Mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r,2]; + r3 = Mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++,3]; + + // the final round is a simple function of S + + C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r,0]; + C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r,1]; + C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r,2]; + C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r,3]; + + } + + private void DecryptBlock(int[,] KW) + { + int r, 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((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r,0]; + r1 = Inv_Mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r,1]; + r2 = Inv_Mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r,2]; + r3 = Inv_Mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r--,3]; + C0 = Inv_Mcol((Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24)) ^ KW[r,0]; + C1 = Inv_Mcol((Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24)) ^ KW[r,1]; + C2 = Inv_Mcol((Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r,2]; + C3 = Inv_Mcol((Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24)) ^ KW[r--,3]; + } + + r0 = Inv_Mcol((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r,0]; + r1 = Inv_Mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r,1]; + r2 = Inv_Mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r,2]; + r3 = Inv_Mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r,3]; + + // the final round's table is a simple function of Si + + C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0,0]; + C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0,1]; + C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0,2]; + C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0,3]; + } + } + +} diff --git a/src/core/srcbc/crypto/engines/AesWrapEngine.cs b/src/core/srcbc/crypto/engines/AesWrapEngine.cs new file mode 100644 index 0000000..bfba7cd --- /dev/null +++ b/src/core/srcbc/crypto/engines/AesWrapEngine.cs @@ -0,0 +1,16 @@ +namespace Org.BouncyCastle.Crypto.Engines +{ + ///+ * Note: + *
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf + *
+ * 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 + *
+ */ + 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 bit long"); + + cnt = 0; + + uint[] w = new uint[1280]; + + for (int i = 0; i < 16; i++) + { + w[i >> 3] |= ((uint)key[i] << (i & 0x7)); + } + Array.Copy(w, 0, w, 4, 4); + + for (int i = 0; i < iv.Length && i < 16; i++) + { + w[(i >> 3) + 8] |= ((uint)iv[i] << (i & 0x7)); + } + 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) + { + uint step = Step(); + buf[3] = (byte)step; + step >>= 8; + buf[2] = (byte)step; + step >>= 8; + buf[1] = (byte)step; + step >>= 8; + buf[0] = (byte)step; + } + 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/src/core/srcbc/crypto/engines/HC256Engine.cs b/src/core/srcbc/crypto/engines/HC256Engine.cs new file mode 100644 index 0000000..eff3ebc --- /dev/null +++ b/src/core/srcbc/crypto/engines/HC256Engine.cs @@ -0,0 +1,207 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +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. + *+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf + *
+ * 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 + *
+ */ + 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) + throw new ArgumentException("The key must be 256 bit long"); + + cnt = 0; + + uint[] w = new uint[2560]; + + for (int i = 0; i < 32; i++) + { + w[i >> 3] |= ((uint)key[i] << (i & 0x7)); + } + + for (int i = 0; i < iv.Length && i < 32; i++) + { + w[(i >> 3) + 8] |= ((uint)iv[i] << (i & 0x7)); + } + + 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) + { + uint step = Step(); + buf[3] = (byte)step; + step >>= 8; + buf[2] = (byte)step; + step >>= 8; + buf[1] = (byte)step; + step >>= 8; + buf[0] = (byte)step; + } + 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/src/core/srcbc/crypto/engines/ISAACEngine.cs b/src/core/srcbc/crypto/engines/ISAACEngine.cs new file mode 100644 index 0000000..50e9def --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/engines/IdeaEngine.cs b/src/core/srcbc/crypto/engines/IdeaEngine.cs new file mode 100644 index 0000000..247b91a --- /dev/null +++ b/src/core/srcbc/crypto/engines/IdeaEngine.cs @@ -0,0 +1,333 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * A class that provides a basic International Data Encryption Algorithm (IDEA) engine. + *+ * 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!). + *
+ *+ * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/ + *
+ *+ * Note: 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 + * www.mediacrypt.com for + * further details. This announcement has been included at the request of + * the patent holders. + *
+ */ + 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. + *+ * i.e. x * MulInv(x) == 1 (modulo BASE) + *
+ */ + 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. + *+ * i.e. x + AddInv(x) == 0 + *
+ */ + 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)); + } + } + } +} diff --git a/src/core/srcbc/crypto/engines/IesEngine.cs b/src/core/srcbc/crypto/engines/IesEngine.cs new file mode 100644 index 0000000..9282770 --- /dev/null +++ b/src/core/srcbc/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 Check that this is right (...Unsigned? Check length?) + byte[] zBytes = z.ToByteArray(); + + return forEncryption + ? EncryptBlock(input, inOff, inLen, zBytes) + : DecryptBlock(input, inOff, inLen, zBytes); + } + } + +} diff --git a/src/core/srcbc/crypto/engines/NaccacheSternEngine.cs b/src/core/srcbc/crypto/engines/NaccacheSternEngine.cs new file mode 100644 index 0000000..94355e2 --- /dev/null +++ b/src/core/srcbc/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 ArrayList[] 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) + { + Console.WriteLine("Constructing lookup Array"); + } + NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key; + ArrayList primes = priv.SmallPrimes; + lookup = new ArrayList[primes.Count]; + for (int i = 0; i < primes.Count; i++) + { + BigInteger actualPrime = (BigInteger) primes[i]; + int actualPrimeValue = actualPrime.IntValue; + + lookup[i] = new ArrayList(actualPrimeValue); + lookup[i].Add(BigInteger.One); + + if (debug) + { + Console.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) + { + Console.WriteLine("input as BigInteger: " + input); + } + + byte[] output; + if (forEncryption) + { + output = Encrypt(input); + } + else + { + ArrayList plain = new ArrayList(); + NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key; + ArrayList primes = priv.SmallPrimes; + // 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); + ArrayList al = lookup[i]; + if (lookup[i].Count != ((BigInteger)primes[i]).IntValue) + { + if (debug) + { + Console.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) + { + Console.WriteLine("Actual prime is " + primes[i]); + Console.WriteLine("Decrypted value is " + exp); + + Console.WriteLine("LookupList for " + primes[i] + " with size " + lookup[i].Count + + " is: "); + for (int j = 0; j < lookup[i].Count; j++) + { + Console.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) + { + Console.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) + { + Console.WriteLine("c(m1) as BigInteger:....... " + m1Crypt); + Console.WriteLine("c(m2) as BigInteger:....... " + m2Crypt); + Console.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) + { + Console.WriteLine(); + } + if (data.Length > GetInputBlockSize()) + { + int inBlocksize = GetInputBlockSize(); + int outBlocksize = GetOutputBlockSize(); + if (debug) + { + Console.WriteLine("Input blocksize is: " + inBlocksize + " bytes"); + Console.WriteLine("Output blocksize is: " + outBlocksize + " bytes"); + Console.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) + { + Console.WriteLine("new datapos is " + datapos); + } + if (tmp != null) + { + tmp.CopyTo(retval, retpos); + retpos += tmp.Length; + } + else + { + if (debug) + { + Console.WriteLine("cipher returned null"); + } + throw new InvalidCipherTextException("cipher returned null"); + } + } + byte[] ret = new byte[retpos]; + Array.Copy(retval, 0, ret, 0, retpos); + if (debug) + { + Console.WriteLine("returning " + ret.Length + " bytes"); + } + return ret; + } + else + { + if (debug) + { + Console.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(ArrayList congruences, ArrayList 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 b_ = b.ModInverse(a); + BigInteger tmp = b.Multiply(b_); + tmp = tmp.Multiply((BigInteger)congruences[i]); + retval = retval.Add(tmp); + } + + return retval.Mod(all); + } + } +} diff --git a/src/core/srcbc/crypto/engines/NoekeonEngine.cs b/src/core/srcbc/crypto/engines/NoekeonEngine.cs new file mode 100644 index 0000000..618007f --- /dev/null +++ b/src/core/srcbc/crypto/engines/NoekeonEngine.cs @@ -0,0 +1,256 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +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] = bytesToIntBig(key, 0); + subKeys[1] = bytesToIntBig(key, 4); + subKeys[2] = bytesToIntBig(key, 8); + subKeys[3] = bytesToIntBig(key, 12); + } + + private int encryptBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + state[0] = bytesToIntBig(input, inOff); + state[1] = bytesToIntBig(input, inOff+4); + state[2] = bytesToIntBig(input, inOff+8); + state[3] = bytesToIntBig(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); + + intToBytesBig(state[0], output, outOff); + intToBytesBig(state[1], output, outOff+4); + intToBytesBig(state[2], output, outOff+8); + intToBytesBig(state[3], output, outOff+12); + + return GenericSize; + } + + private int decryptBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + state[0] = bytesToIntBig(input, inOff); + state[1] = bytesToIntBig(input, inOff+4); + state[2] = bytesToIntBig(input, inOff+8); + state[3] = bytesToIntBig(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]; + + intToBytesBig(state[0], output, outOff); + intToBytesBig(state[1], output, outOff+4); + intToBytesBig(state[2], output, outOff+8); + intToBytesBig(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 bytesToIntBig(byte[] input, int off) + { + int result = ((input[off++]) << 24) | + ((input[off++] & 0xff) << 16) | + ((input[off++] & 0xff) << 8) | + (input[off ] & 0xff); + return (uint) result; + } + + private void intToBytesBig(uint x, byte[] output, int off) + { + output[off++] = (byte)(x >> 24); + output[off++] = (byte)(x >> 16); + output[off++] = (byte)(x >> 8); + output[off ] = (byte)x; + } + + private uint rotl(uint x, int y) + { + return (x << y) | (x >> (32-y)); + } + } +} diff --git a/src/core/srcbc/crypto/engines/NullEngine.cs b/src/core/srcbc/crypto/engines/NullEngine.cs new file mode 100644 index 0000000..22a4e4c --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/engines/RC2Engine.cs b/src/core/srcbc/crypto/engines/RC2Engine.cs new file mode 100644 index 0000000..3f3a513 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/engines/RC2WrapEngine.cs b/src/core/srcbc/crypto/engines/RC2WrapEngine.cs new file mode 100644 index 0000000..722f91f --- /dev/null +++ b/src/core/srcbc/crypto/engines/RC2WrapEngine.cs @@ -0,0 +1,372 @@ +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) + { + byte[] result = new byte[8]; + + sha1.BlockUpdate(key, 0, key.Length); + sha1.DoFinal(digest, 0); + + 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.AreEqual(CalculateCmsKeyChecksum(key), checksum); + } + } +} diff --git a/src/core/srcbc/crypto/engines/RC4Engine.cs b/src/core/srcbc/crypto/engines/RC4Engine.cs new file mode 100644 index 0000000..76bae34 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/engines/RC532Engine.cs b/src/core/srcbc/crypto/engines/RC532Engine.cs new file mode 100644 index 0000000..3f34031 --- /dev/null +++ b/src/core/srcbc/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 theRC5 Encryption Algorithm
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * http://www.rsasecurity.com/rsalabs/cryptobytes.
+ * + * This implementation has a word size of 32 bits.
+ */ + 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 x is rotated left by y bits. + * Only the lg(32) low-order bits of y + * 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 x is rotated left by y bits. + * Only the lg(32) low-order bits of y + * 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/src/core/srcbc/crypto/engines/RC564Engine.cs b/src/core/srcbc/crypto/engines/RC564Engine.cs new file mode 100644 index 0000000..7aee798 --- /dev/null +++ b/src/core/srcbc/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 theRC5 Encryption Algorithm
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * http://www.rsasecurity.com/rsalabs/cryptobytes.
+ * + * This implementation is set to work with a 64 bit word size.
+ */ + 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 x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * 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 x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * 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/src/core/srcbc/crypto/engines/RC6Engine.cs b/src/core/srcbc/crypto/engines/RC6Engine.cs new file mode 100644 index 0000000..c1c23e0 --- /dev/null +++ b/src/core/srcbc/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 x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * 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 x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * 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/src/core/srcbc/crypto/engines/RFC3211WrapEngine.cs b/src/core/srcbc/crypto/engines/RFC3211WrapEngine.cs new file mode 100644 index 0000000..0b537a1 --- /dev/null +++ b/src/core/srcbc/crypto/engines/RFC3211WrapEngine.cs @@ -0,0 +1,166 @@ +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]); + + for (int i = 0; i != 3; i++) + { + byte check = (byte)~cekBlock[1 + i]; + if (check != key[i]) + { + throw new InvalidCipherTextException("wrapped key fails checksum"); + } + } + + return key; + } + } +} diff --git a/src/core/srcbc/crypto/engines/RFC3394WrapEngine.cs b/src/core/srcbc/crypto/engines/RFC3394WrapEngine.cs new file mode 100644 index 0000000..b40efec --- /dev/null +++ b/src/core/srcbc/crypto/engines/RFC3394WrapEngine.cs @@ -0,0 +1,181 @@ +using System; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + ///+ * Note: this implementation is based on information prior to readonly NIST publication. + *
+ */ + public class RijndaelEngine + : IBlockCipher + { + private static readonly int MAXROUNDS = 14; + + private static readonly int MAXKC = (256/4); + + private static readonly byte[] Logtable = { + (byte)0, (byte)0, (byte)25, (byte)1, (byte)50, (byte)2, (byte)26, (byte)198, + (byte)75, (byte)199, (byte)27, (byte)104, (byte)51, (byte)238, (byte)223, (byte)3, + (byte)100, (byte)4, (byte)224, (byte)14, (byte)52, (byte)141, (byte)129, (byte)239, + (byte)76, (byte)113, (byte)8, (byte)200, (byte)248, (byte)105, (byte)28, (byte)193, + (byte)125, (byte)194, (byte)29, (byte)181, (byte)249, (byte)185, (byte)39, (byte)106, + (byte)77, (byte)228, (byte)166, (byte)114, (byte)154, (byte)201, (byte)9, (byte)120, + (byte)101, (byte)47, (byte)138, (byte)5, (byte)33, (byte)15, (byte)225, (byte)36, + (byte)18, (byte)240, (byte)130, (byte)69, (byte)53, (byte)147, (byte)218, (byte)142, + (byte)150, (byte)143, (byte)219, (byte)189, (byte)54, (byte)208, (byte)206, (byte)148, + (byte)19, (byte)92, (byte)210, (byte)241, (byte)64, (byte)70, (byte)131, (byte)56, + (byte)102, (byte)221, (byte)253, (byte)48, (byte)191, (byte)6, (byte)139, (byte)98, + (byte)179, (byte)37, (byte)226, (byte)152, (byte)34, (byte)136, (byte)145, (byte)16, + (byte)126, (byte)110, (byte)72, (byte)195, (byte)163, (byte)182, (byte)30, (byte)66, + (byte)58, (byte)107, (byte)40, (byte)84, (byte)250, (byte)133, (byte)61, (byte)186, + (byte)43, (byte)121, (byte)10, (byte)21, (byte)155, (byte)159, (byte)94, (byte)202, + (byte)78, (byte)212, (byte)172, (byte)229, (byte)243, (byte)115, (byte)167, (byte)87, + (byte)175, (byte)88, (byte)168, (byte)80, (byte)244, (byte)234, (byte)214, (byte)116, + (byte)79, (byte)174, (byte)233, (byte)213, (byte)231, (byte)230, (byte)173, (byte)232, + (byte)44, (byte)215, (byte)117, (byte)122, (byte)235, (byte)22, (byte)11, (byte)245, + (byte)89, (byte)203, (byte)95, (byte)176, (byte)156, (byte)169, (byte)81, (byte)160, + (byte)127, (byte)12, (byte)246, (byte)111, (byte)23, (byte)196, (byte)73, (byte)236, + (byte)216, (byte)67, (byte)31, (byte)45, (byte)164, (byte)118, (byte)123, (byte)183, + (byte)204, (byte)187, (byte)62, (byte)90, (byte)251, (byte)96, (byte)177, (byte)134, + (byte)59, (byte)82, (byte)161, (byte)108, (byte)170, (byte)85, (byte)41, (byte)157, + (byte)151, (byte)178, (byte)135, (byte)144, (byte)97, (byte)190, (byte)220, (byte)252, + (byte)188, (byte)149, (byte)207, (byte)205, (byte)55, (byte)63, (byte)91, (byte)209, + (byte)83, (byte)57, (byte)132, (byte)60, (byte)65, (byte)162, (byte)109, (byte)71, + (byte)20, (byte)42, (byte)158, (byte)93, (byte)86, (byte)242, (byte)211, (byte)171, + (byte)68, (byte)17, (byte)146, (byte)217, (byte)35, (byte)32, (byte)46, (byte)137, + (byte)180, (byte)124, (byte)184, (byte)38, (byte)119, (byte)153, (byte)227, (byte)165, + (byte)103, (byte)74, (byte)237, (byte)222, (byte)197, (byte)49, (byte)254, (byte)24, + (byte)13, (byte)99, (byte)140, (byte)128, (byte)192, (byte)247, (byte)112, (byte)7 + }; + + private static readonly byte[] Alogtable = { + (byte)0, (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53, + (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170, + (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49, + (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205, + (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136, + (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154, + (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163, + (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160, + (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65, + (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117, + (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128, + (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84, + (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202, + (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14, + (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23, + (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1, + (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53, + (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170, + (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49, + (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205, + (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136, + (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154, + (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163, + (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160, + (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65, + (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117, + (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128, + (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84, + (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202, + (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14, + (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23, + (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1, + }; + + private static readonly byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + private static readonly byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + private static readonly int[] 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/src/core/srcbc/crypto/engines/RsaEngine.cs b/src/core/srcbc/crypto/engines/RsaEngine.cs new file mode 100644 index 0000000..91fae9f --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/engines/SEEDEngine.cs b/src/core/srcbc/crypto/engines/SEEDEngine.cs new file mode 100644 index 0000000..6e66dbb --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/engines/SEEDWrapEngine.cs b/src/core/srcbc/crypto/engines/SEEDWrapEngine.cs new file mode 100644 index 0000000..7b2ca9f --- /dev/null +++ b/src/core/srcbc/crypto/engines/SEEDWrapEngine.cs @@ -0,0 +1,16 @@ +namespace Org.BouncyCastle.Crypto.Engines +{ + ///+ * Serpent was designed by Ross Anderson, Eli Biham and Lars Knudsen as a + * candidate algorithm for the NIST AES Quest.> + *
+ *+ * For full details see the The Serpent home page + *
+ */ + 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. + *+ * For further details see: + * http://fp.gladman.plus.com/cryptography_technology/serpent/ + *
+ */ + + /* 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/src/core/srcbc/crypto/engines/SkipjackEngine.cs b/src/core/srcbc/crypto/engines/SkipjackEngine.cs new file mode 100644 index 0000000..12b848e --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/engines/TEAEngine.cs b/src/core/srcbc/crypto/engines/TEAEngine.cs new file mode 100644 index 0000000..4733282 --- /dev/null +++ b/src/core/srcbc/crypto/engines/TEAEngine.cs @@ -0,0 +1,191 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * An TEA engine. + */ + public class TeaEngine + : IBlockCipher + { + private const int + rounds = 32, + block_size = 8, + key_size = 16, + delta = unchecked((int) 0x9E3779B9), + d_sum = unchecked((int) 0xC6EF3720); // sum on decrypt + + /* + * the expanded key array of 4 subkeys + */ + private int _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 = bytesToInt(key, 0); + _b = bytesToInt(key, 4); + _c = bytesToInt(key, 8); + _d = bytesToInt(key, 12); + } + + private int encryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(inBytes, inOff); + int v1 = bytesToInt(inBytes, inOff + 4); + + int sum = 0; + + for (int i = 0; i != rounds; i++) + { + sum += delta; +// v0 += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b); + v0 += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((int)((uint)v1 >> 5) + _b); +// v1 += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d); + v1 += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((int)((uint)v0 >> 5) + _d); + } + + unpackInt(v0, outBytes, outOff); + unpackInt(v1, outBytes, outOff + 4); + + return block_size; + } + + private int decryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(inBytes, inOff); + int v1 = bytesToInt(inBytes, inOff + 4); + + int sum = d_sum; + + for (int i = 0; i != rounds; i++) + { +// v1 -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d); + v1 -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((int)((uint)v0 >> 5) + _d); +// v0 -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b); + v0 -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((int)((uint)v1 >> 5) + _b); + sum -= delta; + } + + unpackInt(v0, outBytes, outOff); + unpackInt(v1, outBytes, outOff + 4); + + return block_size; + } + + private int bytesToInt( + byte[] b, + int inOff) + { + return ((b[inOff++]) << 24) + | ((b[inOff++] & 255) << 16) + | ((b[inOff++] & 255) << 8) + | ((b[inOff] & 255)); + } + + private void unpackInt( + int v, + byte[] b, + int outOff) + { + uint uv = (uint) v; + b[outOff++] = (byte)(uv >> 24); + b[outOff++] = (byte)(uv >> 16); + b[outOff++] = (byte)(uv >> 8); + b[outOff ] = (byte)uv; + } + } +} diff --git a/src/core/srcbc/crypto/engines/TwofishEngine.cs b/src/core/srcbc/crypto/engines/TwofishEngine.cs new file mode 100644 index 0000000..617b0a3 --- /dev/null +++ b/src/core/srcbc/crypto/engines/TwofishEngine.cs @@ -0,0 +1,673 @@ +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+ *
+ * G(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1 + *+ * where a = primitive root of field generator 0x14D + * + */ + 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/src/core/srcbc/crypto/engines/VMPCEngine.cs b/src/core/srcbc/crypto/engines/VMPCEngine.cs new file mode 100644 index 0000000..df79593 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/engines/VMPCKSA3Engine.cs b/src/core/srcbc/crypto/engines/VMPCKSA3Engine.cs new file mode 100644 index 0000000..7e0c64b --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/engines/XTEAEngine.cs b/src/core/srcbc/crypto/engines/XTEAEngine.cs new file mode 100644 index 0000000..0d4036f --- /dev/null +++ b/src/core/srcbc/crypto/engines/XTEAEngine.cs @@ -0,0 +1,185 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +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), + d_sum = unchecked((int) 0xC6EF3720); // sum on decrypt + + /* + * the expanded key array of 4 subkeys + */ + private int[] _S = new int[4]; + private bool _initialised; + private bool _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) + { + _S[0] = bytesToInt(key, 0); + _S[1] = bytesToInt(key, 4); + _S[2] = bytesToInt(key, 8); + _S[3] = bytesToInt(key, 12); + } + + private int encryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(inBytes, inOff); + int v1 = bytesToInt(inBytes, inOff + 4); + + int sum = 0; + + for (int i = 0; i != rounds; i++) + { + v0 += ((v1 << 4 ^ (int)((uint)v1 >> 5)) + v1) ^ (sum + _S[sum & 3]); + sum += delta; + v1 += ((v0 << 4 ^ (int)((uint)v0 >> 5)) + v0) ^ (sum + _S[(int)((uint)sum >> 11) & 3]); + } + + unpackInt(v0, outBytes, outOff); + unpackInt(v1, outBytes, outOff + 4); + + return block_size; + } + + private int decryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(inBytes, inOff); + int v1 = bytesToInt(inBytes, inOff + 4); + + int sum = d_sum; + + for (int i = 0; i != rounds; i++) + { + v1 -= ((v0 << 4 ^ (int)((uint)v0 >> 5)) + v0) ^ (sum + _S[(int)((uint)sum >> 11) & 3]); + sum -= delta; + v0 -= ((v1 << 4 ^ (int)((uint)v1 >> 5)) + v1) ^ (sum + _S[sum & 3]); + } + + unpackInt(v0, outBytes, outOff); + unpackInt(v1, outBytes, outOff + 4); + + return block_size; + } + + private int bytesToInt(byte[] b, int inOff) + { + return ((b[inOff++]) << 24) | + ((b[inOff++] & 255) << 16) | + ((b[inOff++] & 255) << 8) | + ((b[inOff] & 255)); + } + + private void unpackInt( + int v, + byte[] b, + int outOff) + { + uint uv = (uint) v; + b[outOff++] = (byte)(uv >> 24); + b[outOff++] = (byte)(uv >> 16); + b[outOff++] = (byte)(uv >> 8); + b[outOff ] = (byte)uv; + } + } +} diff --git a/src/core/srcbc/crypto/generators/BaseKdfBytesGenerator.cs b/src/core/srcbc/crypto/generators/BaseKdfBytesGenerator.cs new file mode 100644 index 0000000..56e23d4 --- /dev/null +++ b/src/core/srcbc/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 + *
+ * Note: can take a while...
+ */ + 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/src/core/srcbc/crypto/generators/DHParametersHelper.cs b/src/core/srcbc/crypto/generators/DHParametersHelper.cs new file mode 100644 index 0000000..25076c1 --- /dev/null +++ b/src/core/srcbc/crypto/generators/DHParametersHelper.cs @@ -0,0 +1,244 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +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} + 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 + internal static BigInteger SelectGenerator( + BigInteger p, + BigInteger q, + SecureRandom random) + { + BigInteger pMinusTwo = p.Subtract(BigInteger.Two); + BigInteger g; + + // Handbook of Applied Cryptography 4.86 + do + { + g = CreateInRange(BigInteger.Two, pMinusTwo, random); + } + while (g.ModPow(BigInteger.Two, p).Equals(BigInteger.One) + || g.ModPow(q, p).Equals(BigInteger.One)); + +/* + // RFC 2631 2.1.1 (and see Handbook of Applied Cryptography 4.81) + do + { + BigInteger h = CreateInRange(BigInteger.Two, pMinusTwo, random); + + g = h.ModPow(BigInteger.Two, p); + } + while (g.Equals(BigInteger.One)); +*/ + + return g; + } + + private static BigInteger CreateInRange( + BigInteger min, + BigInteger max, + SecureRandom random) + { + BigInteger x; + do + { + x = new BigInteger(max.BitLength, random); + } + while (x.CompareTo(min) < 0 || x.CompareTo(max) > 0); + return x; + } + } +} diff --git a/src/core/srcbc/crypto/generators/DesEdeKeyGenerator.cs b/src/core/srcbc/crypto/generators/DesEdeKeyGenerator.cs new file mode 100644 index 0000000..10ef4e7 --- /dev/null +++ b/src/core/srcbc/crypto/generators/DesEdeKeyGenerator.cs @@ -0,0 +1,66 @@ +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) + { + base.engineInit(parameters); + + 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/src/core/srcbc/crypto/generators/DesKeyGenerator.cs b/src/core/srcbc/crypto/generators/DesKeyGenerator.cs new file mode 100644 index 0000000..12dcc9d --- /dev/null +++ b/src/core/srcbc/crypto/generators/DesKeyGenerator.cs @@ -0,0 +1,34 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class DesKeyGenerator + : CipherKeyGenerator + { + public DesKeyGenerator() + { + } + + internal DesKeyGenerator( + int defaultStrength) + : base(defaultStrength) + { + } + + 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/src/core/srcbc/crypto/generators/DsaKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/DsaKeyPairGenerator.cs new file mode 100644 index 0000000..c231ece --- /dev/null +++ b/src/core/srcbc/crypto/generators/DsaKeyPairGenerator.cs @@ -0,0 +1,56 @@ +using System; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * a DSA key pair generator. + * + * This Generates DSA keys in line with the method described + * in FIPS 186-2. + */ + 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; + SecureRandom random = param.Random; + + BigInteger q = dsaParams.Q; + BigInteger x; + + do + { + x = new BigInteger(160, random); + } + while (x.SignValue == 0 || x.CompareTo(q) >= 0); + + // + // calculate the public key. + // + BigInteger y = dsaParams.G.ModPow(x, dsaParams.P); + + return new AsymmetricCipherKeyPair( + new DsaPublicKeyParameters(y, dsaParams), + new DsaPrivateKeyParameters(x, dsaParams)); + } + } +} diff --git a/src/core/srcbc/crypto/generators/DsaParametersGenerator.cs b/src/core/srcbc/crypto/generators/DsaParametersGenerator.cs new file mode 100644 index 0000000..fbe8942 --- /dev/null +++ b/src/core/srcbc/crypto/generators/DsaParametersGenerator.cs @@ -0,0 +1,184 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Generate suitable parameters for DSA, in line with FIPS 186-2. + */ + public class DsaParametersGenerator + { + private int size; + 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"); + + this.size = size; + 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. + *+ * Note: can take a while...
+ */ + public DsaParameters GenerateParameters() + { + 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 = (size - 1) / 160; + byte[] w = new byte[size / 8]; + + BigInteger q = null, p = null, g = null; + int counter = 0; + bool primesFound = false; + + while (!primesFound) + { + do + { + random.NextBytes(seed); + + sha1.BlockUpdate(seed, 0, seed.Length); + + sha1.DoFinal(part1, 0); + + Array.Copy(seed, 0, part2, 0, seed.Length); + + Add(part2, seed, 1); + + sha1.BlockUpdate(part2, 0, part2.Length); + + sha1.DoFinal(part2, 0); + + for (int i = 0; i != u.Length; i++) + { + u[i] = (byte)(part1[i] ^ part2[i]); + } + + u[0] |= (byte)0x80; + u[19] |= (byte)0x01; + + q = new BigInteger(1, u); + } + while (!q.IsProbablePrime(certainty)); + + counter = 0; + + int offset = 2; + + while (counter < 4096) + { + for (int k = 0; k < n; k++) + { + Add(part1, seed, offset + k); + sha1.BlockUpdate(part1, 0, part1.Length); + sha1.DoFinal(part1, 0); + Array.Copy(part1, 0, w, w.Length - (k + 1) * part1.Length, part1.Length); + } + + Add(part1, seed, offset + n); + sha1.BlockUpdate(part1, 0, part1.Length); + sha1.DoFinal(part1, 0); + 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)); + + p = x.Subtract(c.Subtract(BigInteger.One)); + + if (p.TestBit(size - 1)) + { + if (p.IsProbablePrime(certainty)) + { + primesFound = true; + break; + } + } + + counter += 1; + offset += n + 1; + } + } + + // + // calculate the generator g + // + BigInteger pMinusOneOverQ = p.Subtract(BigInteger.One).Divide(q); + + for (;;) + { + BigInteger h = new BigInteger(size, random); + if (h.CompareTo(BigInteger.One) <= 0 || h.CompareTo(p.Subtract(BigInteger.One)) >= 0) + { + continue; + } + + g = h.ModPow(pMinusOneOverQ, p); + if (g.CompareTo(BigInteger.One) <= 0) + { + continue; + } + + break; + } + + return new DsaParameters(p, q, g, new DsaValidationParameters(seed, counter)); + } + + private static bool IsValidDsaStrength( + int strength) + { + return strength >= 512 && strength <= 1024 && strength % 64 == 0; + } + } +} diff --git a/src/core/srcbc/crypto/generators/ECKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/ECKeyPairGenerator.cs new file mode 100644 index 0000000..7d7718c --- /dev/null +++ b/src/core/srcbc/crypto/generators/ECKeyPairGenerator.cs @@ -0,0 +1,130 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +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; + + if (ecP.PublicKeyParamSet != null) + { + if (algorithm != "ECGOST3410") + throw new ArgumentException("parameters invalid for algorithm: " + algorithm, "parameters"); + + this.publicKeyParamSet = ecP.PublicKeyParamSet; + } + + this.parameters = ecP.DomainParameters; + } + else + { + DerObjectIdentifier oid; + switch (parameters.Strength) + { + case 192: + oid = X9ObjectIdentifiers.Prime192v1; + break; + case 239: + oid = X9ObjectIdentifiers.Prime239v1; + break; + case 256: + oid = X9ObjectIdentifiers.Prime256v1; + break; + default: + throw new InvalidParameterException("unknown key size."); + } + + X9ECParameters ecps = X962NamedCurves.GetByOid(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(q, publicKeyParamSet), + new ECPrivateKeyParameters(d, publicKeyParamSet)); + } + + return new AsymmetricCipherKeyPair( + new ECPublicKeyParameters(algorithm, q, parameters), + new ECPrivateKeyParameters(algorithm, d, parameters)); + } + + private string VerifyAlgorithmName( + string algorithm) + { + string upper = algorithm.ToUpper(CultureInfo.InvariantCulture); + + switch (upper) + { + case "EC": + case "ECDSA": + case "ECGOST3410": + case "ECDH": + case "ECDHC": + break; + default: + throw new ArgumentException("unrecognised algorithm: " + algorithm, "algorithm"); + } + + return upper; + } + } +} diff --git a/src/core/srcbc/crypto/generators/ElGamalKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/ElGamalKeyPairGenerator.cs new file mode 100644 index 0000000..9e3f54d --- /dev/null +++ b/src/core/srcbc/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. + *+ * This Generates keys consistent for use with ElGamal as described in + * page 164 of "Handbook of Applied Cryptography".
+ */ + public class ElGamalKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private ElGamalKeyGenerationParameters param; + + public void Init( + KeyGenerationParameters parameters) + { + this.param = (ElGamalKeyGenerationParameters) parameters; + } + + public AsymmetricCipherKeyPair GenerateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance; + ElGamalParameters elParams = param.Parameters; + + BigInteger p = elParams.P; + BigInteger x = helper.CalculatePrivate(p, param.Random, elParams.L); + BigInteger y = helper.CalculatePublic(p, elParams.G, x); + + return new AsymmetricCipherKeyPair( + new ElGamalPublicKeyParameters(y, elParams), + new ElGamalPrivateKeyParameters(x, elParams)); + } + } + +} diff --git a/src/core/srcbc/crypto/generators/ElGamalParametersGenerator.cs b/src/core/srcbc/crypto/generators/ElGamalParametersGenerator.cs new file mode 100644 index 0000000..1bb9f4e --- /dev/null +++ b/src/core/srcbc/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. + *+ * Note: can take a while... + *
+ */ + 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/src/core/srcbc/crypto/generators/GOST3410KeyPairGenerator.cs b/src/core/srcbc/crypto/generators/GOST3410KeyPairGenerator.cs new file mode 100644 index 0000000..e8bc48d --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/generators/GOST3410ParametersGenerator.cs b/src/core/srcbc/crypto/generators/GOST3410ParametersGenerator.cs new file mode 100644 index 0000000..01d4a63 --- /dev/null +++ b/src/core/srcbc/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+ * The scheme is a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an + * iteration count of 1. + *
+ */ + 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/src/core/srcbc/crypto/generators/Pkcs12ParametersGenerator.cs b/src/core/srcbc/crypto/generators/Pkcs12ParametersGenerator.cs new file mode 100644 index 0000000..9fc1f67 --- /dev/null +++ b/src/core/srcbc/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. + *+ * The document this implementation is based on can be found at + * + * RSA's Pkcs12 Page + *
+ */ + 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/src/core/srcbc/crypto/generators/Pkcs5S1ParametersGenerator.cs b/src/core/srcbc/crypto/generators/Pkcs5S1ParametersGenerator.cs new file mode 100644 index 0000000..5fa21f1 --- /dev/null +++ b/src/core/srcbc/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. + *+ * The document this implementation is based on can be found at + * + * RSA's Pkcs5 Page + *
+ */ + 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/src/core/srcbc/crypto/generators/Pkcs5S2ParametersGenerator.cs b/src/core/srcbc/crypto/generators/Pkcs5S2ParametersGenerator.cs new file mode 100644 index 0000000..dbe0be0 --- /dev/null +++ b/src/core/srcbc/crypto/generators/Pkcs5S2ParametersGenerator.cs @@ -0,0 +1,175 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Macs; +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. + *+ * The document this implementation is based on can be found at + * + * RSA's Pkcs5 Page
+ */ + public class Pkcs5S2ParametersGenerator + : PbeParametersGenerator + { + private readonly IMac hMac = new HMac(new Sha1Digest()); + + /** + * construct a Pkcs5 Scheme 2 Parameters generator. + */ + public Pkcs5S2ParametersGenerator() + { + } + + 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 void IntToOctet( + byte[] Buffer, + int i) + { + Buffer[0] = (byte)((uint) i >> 24); + Buffer[1] = (byte)((uint) i >> 16); + Buffer[2] = (byte)((uint) i >> 8); + Buffer[3] = (byte)i; + } + + 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++) + { + IntToOctet(iBuf, i); + + 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/src/core/srcbc/crypto/generators/RSABlindingFactorGenerator.cs b/src/core/srcbc/crypto/generators/RSABlindingFactorGenerator.cs new file mode 100644 index 0000000..1583bd0 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/generators/RsaKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/RsaKeyPairGenerator.cs new file mode 100644 index 0000000..41fe687 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/io/CipherStream.cs b/src/core/srcbc/crypto/io/CipherStream.cs new file mode 100644 index 0000000..e69703c --- /dev/null +++ b/src/core/srcbc/crypto/io/CipherStream.cs @@ -0,0 +1,224 @@ +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 pos = offset; +// int end = offset + count; +// try +// { +// while (pos < end) +// { +// if (mInPos >= mInBufEnd && !FillInBuf()) break; +// +// int len = System.Math.Min(end - pos, mInBufEnd - mInPos); +// Array.Copy(mInBuf, mInPos, buffer, pos, len); +// mInPos += len; +// pos += len; +// } +// } +// catch (IOException) +// { +// if (pos == offset) throw; +// } +// return pos - offset; + + // TODO Optimise + int i = 0; + while (i < count) + { + int c = ReadByte(); + + if (c < 0) break; + + buffer[offset + i++] = (byte) c; + } + + return i; + } + 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 value) + { + if (outCipher == null) + { + stream.WriteByte(value); + return; + } + + byte[] data = outCipher.ProcessByte(value); + 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(); } + } + public override void Close() + { + if (outCipher != null) + { + byte[] data = outCipher.DoFinal(); + stream.Write(data, 0, data.Length); + stream.Flush(); + } + stream.Close(); + } + 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 value) { throw new NotSupportedException(); } + } +} diff --git a/src/core/srcbc/crypto/io/DigestStream.cs b/src/core/srcbc/crypto/io/DigestStream.cs new file mode 100644 index 0000000..6a5ab42 --- /dev/null +++ b/src/core/srcbc/crypto/io/DigestStream.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; +using Org.BouncyCastle.Crypto; +namespace Org.BouncyCastle.Crypto.IO +{ + public class DigestStream : Stream + { + internal Stream stream; + internal IDigest inDigest; + internal IDigest outDigest; + + public DigestStream( + Stream stream, + IDigest readDigest, + IDigest writeDigest) + { + this.stream = stream; + this.inDigest = readDigest; + this.outDigest = writeDigest; + } + public IDigest ReadDigest() + { + return inDigest; + } + public IDigest WriteDigest() + { + return outDigest; + } + public override int ReadByte() + { + int b = stream.ReadByte(); + if (inDigest != null) + { + if (b >= 0) + { + inDigest.Update((byte)b); + } + } + return b; + } + + 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 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 value) + { + if (outDigest != null) + { + outDigest.Update(value); + } + stream.WriteByte(value); + } + public override bool CanRead + { + get { return stream.CanRead && (inDigest != null); } + } + public override bool CanWrite + { + get { return stream.CanWrite && (outDigest != null); } + } + 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; } + } + public override void Close() + { + stream.Close(); + } + public override void Flush() + { + stream.Flush(); + } + public override long Seek( + long offset, + SeekOrigin origin) + { + return stream.Seek(offset,origin); + } + public override void SetLength(long value) + { + stream.SetLength(value); + } + } +} diff --git a/src/core/srcbc/crypto/io/MacStream.cs b/src/core/srcbc/crypto/io/MacStream.cs new file mode 100644 index 0000000..5f87b79 --- /dev/null +++ b/src/core/srcbc/crypto/io/MacStream.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; +using Org.BouncyCastle.Crypto; +namespace Org.BouncyCastle.Crypto.IO +{ + public class MacStream : Stream + { + internal Stream stream; + internal IMac inMac; + internal IMac outMac; + + public MacStream( + Stream stream, + IMac readMac, + IMac writeMac) + { + this.stream = stream; + this.inMac = readMac; + this.outMac = writeMac; + } + public IMac ReadMac() + { + return inMac; + } + public IMac WriteMac() + { + return outMac; + } + public override int ReadByte() + { + int b = stream.ReadByte(); + if (inMac != null) + { + if (b >= 0) + { + inMac.Update((byte)b); + } + } + return b; + } + + 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, count); + } + } + return n; + } + 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 value) + { + if (outMac != null) + { + outMac.Update(value); + } + stream.WriteByte(value); + } + public override bool CanRead + { + get { return stream.CanRead && (inMac != null); } + } + public override bool CanWrite + { + get { return stream.CanWrite && (outMac != null); } + } + 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; } + } + public override void Close() + { + stream.Close(); + } + public override void Flush() + { + stream.Flush(); + } + public override long Seek( + long offset, + SeekOrigin origin) + { + return stream.Seek(offset,origin); + } + public override void SetLength(long value) + { + stream.SetLength(value); + } + } +} diff --git a/src/core/srcbc/crypto/macs/CMac.cs b/src/core/srcbc/crypto/macs/CMac.cs new file mode 100644 index 0000000..75b2920 --- /dev/null +++ b/src/core/srcbc/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 + *+ * CMAC is analogous to OMAC1 - see also en.wikipedia.org/wiki/CMAC + *
+ * CMAC is a NIST recomendation - see + * csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf + *
+ * CMAC/OMAC1 is a blockcipher-based message authentication code designed and + * analyzed by Tetsu Iwata and Kaoru Kurosawa. + *
+ * CMAC/OMAC1 is a simple variant of the CBC MAC (Cipher Block Chaining Message + * Authentication Code). OMAC stands for One-Key CBC MAC. + *
+ * 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. + *
+ */ + 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. + * + * 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/src/core/srcbc/crypto/macs/CbcBlockCipherMac.cs b/src/core/srcbc/crypto/macs/CbcBlockCipherMac.cs new file mode 100644 index 0000000..95e8573 --- /dev/null +++ b/src/core/srcbc/crypto/macs/CbcBlockCipherMac.cs @@ -0,0 +1,213 @@ +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[] mac; + private byte[] Buffer; + 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. + *+ * 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. + */ + 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. + *+ * 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. + * @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; + + mac = new byte[cipher.GetBlockSize()]; + + Buffer = 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 == 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(); + + if (padding == null) + { + // pad with zeroes + while (bufOff < blockSize) + { + Buffer[bufOff++] = 0; + } + } + else + { + if (bufOff == blockSize) + { + cipher.ProcessBlock(Buffer, 0, mac, 0); + bufOff = 0; + } + + padding.AddPadding(Buffer, bufOff); + } + + cipher.ProcessBlock(Buffer, 0, mac, 0); + + 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/src/core/srcbc/crypto/macs/CfbBlockCipherMac.cs b/src/core/srcbc/crypto/macs/CfbBlockCipherMac.cs new file mode 100644 index 0000000..16f870a --- /dev/null +++ b/src/core/srcbc/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. + *+ * 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 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. + *+ * 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 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/src/core/srcbc/crypto/macs/GOST28147Mac.cs b/src/core/srcbc/crypto/macs/GOST28147Mac.cs new file mode 100644 index 0000000..c0116a1 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/macs/HMac.cs b/src/core/srcbc/crypto/macs/HMac.cs new file mode 100644 index 0000000..fe12329 --- /dev/null +++ b/src/core/srcbc/crypto/macs/HMac.cs @@ -0,0 +1,141 @@ +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 byte[] inputPad; + private byte[] outputPad; + + public HMac( + IDigest digest) + { + this.digest = digest; + digestSize = digest.GetDigestSize(); + + blockLength = digest.GetByteLength(); + + inputPad = new byte[blockLength]; + 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(); + + if (key.Length > blockLength) + { + digest.BlockUpdate(key, 0, key.Length); + digest.DoFinal(inputPad, 0); + for (int i = digestSize; i < inputPad.Length; i++) + { + inputPad[i] = 0; + } + } + else + { + Array.Copy(key, 0, inputPad, 0, key.Length); + for (int i = key.Length; i < inputPad.Length; i++) + { + inputPad[i] = 0; + } + } + + outputPad = new byte[inputPad.Length]; + Array.Copy(inputPad, 0, outputPad, 0, inputPad.Length); + + for (int i = 0; i < inputPad.Length; i++) + { + inputPad[i] ^= IPAD; + } + + for (int i = 0; i < outputPad.Length; i++) + { + outputPad[i] ^= OPAD; + } + + 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); + + Reset(); + + return len; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + /* + * reset the underlying digest. + */ + digest.Reset(); + + /* + * reinitialize the digest. + */ + digest.BlockUpdate(inputPad, 0, inputPad.Length); + } + } +} diff --git a/src/core/srcbc/crypto/macs/ISO9797Alg3Mac.cs b/src/core/srcbc/crypto/macs/ISO9797Alg3Mac.cs new file mode 100644 index 0000000..a8474ed --- /dev/null +++ b/src/core/srcbc/crypto/macs/ISO9797Alg3Mac.cs @@ -0,0 +1,259 @@ +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. + *+ * 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. + */ + 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. + *+ * 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. + * @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)) + throw new ArgumentException("parameters must be an instance of KeyParameter"); + + // 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 = (KeyParameter)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"); + } + + 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/src/core/srcbc/crypto/macs/VMPCMac.cs b/src/core/srcbc/crypto/macs/VMPCMac.cs new file mode 100644 index 0000000..46450f4 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/modes/CbcBlockCipher.cs b/src/core/srcbc/crypto/modes/CbcBlockCipher.cs new file mode 100644 index 0000000..34b9103 --- /dev/null +++ b/src/core/srcbc/crypto/modes/CbcBlockCipher.cs @@ -0,0 +1,230 @@ +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); + + 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/src/core/srcbc/crypto/modes/CcmBlockCipher.cs b/src/core/srcbc/crypto/modes/CcmBlockCipher.cs new file mode 100644 index 0000000..90451b9 --- /dev/null +++ b/src/core/srcbc/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. + *+ * Note: this mode is a packet mode - it needs all the data up front. + *
+ */ + 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.AreEqual(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/src/core/srcbc/crypto/modes/CfbBlockCipher.cs b/src/core/srcbc/crypto/modes/CfbBlockCipher.cs new file mode 100644 index 0000000..f83e8fa --- /dev/null +++ b/src/core/srcbc/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 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; + } + /** + * 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 plaintext producing the plain text + // + 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/src/core/srcbc/crypto/modes/CtsBlockCipher.cs b/src/core/srcbc/crypto/modes/CtsBlockCipher.cs new file mode 100644 index 0000000..eb1ce14 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/modes/EAXBlockCipher.cs b/src/core/srcbc/crypto/modes/EAXBlockCipher.cs new file mode 100644 index 0000000..a069568 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/modes/GCMBlockCipher.cs b/src/core/srcbc/crypto/modes/GCMBlockCipher.cs new file mode 100644 index 0000000..bdeffe5 --- /dev/null +++ b/src/core/srcbc/crypto/modes/GCMBlockCipher.cs @@ -0,0 +1,447 @@ +using System; + +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes +{ + ///+ * For further info see RFC 2440. + *
+ */ + public class OpenPgpCfbBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] FR; + private byte[] FRE; + private byte[] tmp; + + 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]; + this.tmp = 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++) + { + outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2); + } + + Array.Copy(outBytes, outOff + 2, FR, 0, blockSize - 2); + } + else if (count == 0) + { + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 0; n < blockSize; n++) + { + outBytes[outOff + n] = EncryptByte(input[inOff + n], n); + } + + Array.Copy(outBytes, outOff, FR, 0, blockSize); + + 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++) + { + outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2); + } + + Array.Copy(outBytes, outOff + 2, FR, 0, blockSize - 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) + { + // copy in buffer so that this mode works if in and out are the same + Array.Copy(input, inOff, tmp, 0, blockSize); + + outBytes[outOff] = EncryptByte(tmp[0], blockSize - 2); + outBytes[outOff + 1] = EncryptByte(tmp[1], blockSize - 1); + + Array.Copy(tmp, 0, FR, blockSize - 2, 2); + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + outBytes[outOff + n] = EncryptByte(tmp[n], n - 2); + } + + Array.Copy(tmp, 2, FR, 0, blockSize - 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) + { + Array.Copy(input, inOff, tmp, 0, blockSize); + + cipher.ProcessBlock(FR, 0, FRE, 0); + + outBytes[outOff] = EncryptByte(tmp[0], 0); + outBytes[outOff + 1] = EncryptByte(tmp[1], 1); + + Array.Copy(FR, 2, FR, 0, blockSize - 2); + + FR[blockSize - 2] = tmp[0]; + FR[blockSize - 1] = tmp[1]; + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + FR[n - 2] = input[inOff + n]; + outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2); + } + + count += blockSize; + } + + return blockSize; + } + } +} diff --git a/src/core/srcbc/crypto/modes/SicBlockCipher.cs b/src/core/srcbc/crypto/modes/SicBlockCipher.cs new file mode 100644 index 0000000..9ccf76b --- /dev/null +++ b/src/core/srcbc/crypto/modes/SicBlockCipher.cs @@ -0,0 +1,106 @@ +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); + } + } + + 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/src/core/srcbc/crypto/paddings/BlockCipherPadding.cs b/src/core/srcbc/crypto/paddings/BlockCipherPadding.cs new file mode 100644 index 0000000..2ab64fa --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/paddings/ISO10126d2Padding.cs b/src/core/srcbc/crypto/paddings/ISO10126d2Padding.cs new file mode 100644 index 0000000..890a502 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/paddings/ISO7816d4Padding.cs b/src/core/srcbc/crypto/paddings/ISO7816d4Padding.cs new file mode 100644 index 0000000..1c8c5f0 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/paddings/PaddedBufferedBlockCipher.cs b/src/core/srcbc/crypto/paddings/PaddedBufferedBlockCipher.cs new file mode 100644 index 0000000..6b32fa0 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/PaddedBufferedBlockCipher.cs @@ -0,0 +1,287 @@ +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/src/core/srcbc/crypto/paddings/Pkcs7Padding.cs b/src/core/srcbc/crypto/paddings/Pkcs7Padding.cs new file mode 100644 index 0000000..c72a108 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/Pkcs7Padding.cs @@ -0,0 +1,77 @@ +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/src/core/srcbc/crypto/paddings/TbcPadding.cs b/src/core/srcbc/crypto/paddings/TbcPadding.cs new file mode 100644 index 0000000..d929849 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/TbcPadding.cs @@ -0,0 +1,79 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + + ///+ /// This padding pads the block out compliment of the last bit + /// of the plain text. + ///
+ ///+ /// 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. + ///
+ ///+ * See "Applied + * Cryptography" by Bruce Schneier for more information. + *
+ * @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/src/core/srcbc/crypto/parameters/DsaKeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/DsaKeyGenerationParameters.cs new file mode 100644 index 0000000..622ac7b --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/DsaKeyParameters.cs b/src/core/srcbc/crypto/parameters/DsaKeyParameters.cs new file mode 100644 index 0000000..8b5d508 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DsaKeyParameters.cs @@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaKeyParameters + : AsymmetricKeyParameter + { + private readonly DsaParameters parameters; + + public 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/src/core/srcbc/crypto/parameters/DsaParameters.cs b/src/core/srcbc/crypto/parameters/DsaParameters.cs new file mode 100644 index 0000000..ec349ba --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/DsaPrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/DsaPrivateKeyParameters.cs new file mode 100644 index 0000000..3061b70 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/DsaPublicKeyParameters.cs b/src/core/srcbc/crypto/parameters/DsaPublicKeyParameters.cs new file mode 100644 index 0000000..5334c34 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/DsaValidationParameters.cs b/src/core/srcbc/crypto/parameters/DsaValidationParameters.cs new file mode 100644 index 0000000..635eae1 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/ECDomainParameters.cs b/src/core/srcbc/crypto/parameters/ECDomainParameters.cs new file mode 100644 index 0000000..aaedaf5 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/ECKeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/ECKeyGenerationParameters.cs new file mode 100644 index 0000000..e55eaac --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ECKeyGenerationParameters.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 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(LookupParameters(publicKeyParamSet), random) + { + this.publicKeyParamSet = publicKeyParamSet; + } + + public ECDomainParameters DomainParameters + { + get { return domainParams; } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + + private static ECDomainParameters LookupParameters( + DerObjectIdentifier publicKeyParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + ECDomainParameters p = ECGost3410NamedCurves.GetByOid(publicKeyParamSet); + + if (p == null) + throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet"); + + return p; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ECKeyParameters.cs b/src/core/srcbc/crypto/parameters/ECKeyParameters.cs new file mode 100644 index 0000000..9dab882 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ECKeyParameters.cs @@ -0,0 +1,121 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; + +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(); + } + + private string VerifyAlgorithmName( + string algorithm) + { + string upper = algorithm.ToUpper(CultureInfo.InvariantCulture); + + switch (upper) + { + case "EC": + case "ECDSA": + case "ECGOST3410": + case "ECDH": + case "ECDHC": + break; + default: + throw new ArgumentException("unrecognised algorithm: " + algorithm, "algorithm"); + } + + return upper; + } + + private static ECDomainParameters LookupParameters( + DerObjectIdentifier publicKeyParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + ECDomainParameters p = ECGost3410NamedCurves.GetByOid(publicKeyParamSet); + + if (p == null) + throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet"); + + return p; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ECPrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/ECPrivateKeyParameters.cs new file mode 100644 index 0000000..53f1861 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ECPrivateKeyParameters.cs @@ -0,0 +1,74 @@ +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) + { + } + + 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 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/src/core/srcbc/crypto/parameters/ECPublicKeyParameters.cs b/src/core/srcbc/crypto/parameters/ECPublicKeyParameters.cs new file mode 100644 index 0000000..48dfd0c --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ECPublicKeyParameters.cs @@ -0,0 +1,73 @@ +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) + { + } + + 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 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/src/core/srcbc/crypto/parameters/ElGamalKeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalKeyGenerationParameters.cs new file mode 100644 index 0000000..fb00bf6 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ElGamalKeyGenerationParameters.cs @@ -0,0 +1,25 @@ +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, parameters.P.BitLength) + { + this.parameters = parameters; + } + + public ElGamalParameters Parameters + { + get { return parameters; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ElGamalKeyParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalKeyParameters.cs new file mode 100644 index 0000000..d545a98 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/ElGamalParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalParameters.cs new file mode 100644 index 0000000..9e7799b --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/ElGamalPrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalPrivateKeyParameters.cs new file mode 100644 index 0000000..73dbd8e --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/ElGamalPublicKeyParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalPublicKeyParameters.cs new file mode 100644 index 0000000..799f293 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/GOST3410KeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410KeyGenerationParameters.cs new file mode 100644 index 0000000..3343717 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/GOST3410KeyParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410KeyParameters.cs new file mode 100644 index 0000000..98b5767 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/GOST3410Parameters.cs b/src/core/srcbc/crypto/parameters/GOST3410Parameters.cs new file mode 100644 index 0000000..d4b4029 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/GOST3410PrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410PrivateKeyParameters.cs new file mode 100644 index 0000000..6348c36 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/GOST3410PublicKeyParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410PublicKeyParameters.cs new file mode 100644 index 0000000..7f3286b --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/GOST3410ValidationParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410ValidationParameters.cs new file mode 100644 index 0000000..6ff41c5 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/ISO18033KDFParameters.cs b/src/core/srcbc/crypto/parameters/ISO18033KDFParameters.cs new file mode 100644 index 0000000..e82dc99 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/IesParameters.cs b/src/core/srcbc/crypto/parameters/IesParameters.cs new file mode 100644 index 0000000..eee6be0 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/IesWithCipherParameters.cs b/src/core/srcbc/crypto/parameters/IesWithCipherParameters.cs new file mode 100644 index 0000000..b2f8804 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/KdfParameters.cs b/src/core/srcbc/crypto/parameters/KdfParameters.cs new file mode 100644 index 0000000..7eb4c09 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/KeyParameter.cs b/src/core/srcbc/crypto/parameters/KeyParameter.cs new file mode 100644 index 0000000..96b5708 --- /dev/null +++ b/src/core/srcbc/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/src/core/srcbc/crypto/parameters/MgfParameters.cs b/src/core/srcbc/crypto/parameters/MgfParameters.cs new file mode 100644 index 0000000..5fbc16a --- /dev/null +++ b/src/core/srcbc/crypto/parameters/MgfParameters.cs @@ -0,0 +1,31 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + ///+ * Internal access to the digest is syncrhonized so a single one of these can be shared. + *
+ */ + public class DigestRandomGenerator + : IRandomGenerator + { + private long counter; + private IDigest digest; + private byte[] state; + + public DigestRandomGenerator( + IDigest digest) + { + this.digest = digest; + this.state = new byte[digest.GetDigestSize()]; + this.counter = 1; + } + + public void AddSeedMaterial( + byte[] inSeed) + { + lock (this) + { + DigestUpdate(inSeed); + } + } + + public void AddSeedMaterial( + long rSeed) + { + lock (this) + { + for (int i = 0; i != 8; i++) + { + DigestUpdate((byte)rSeed); +// rSeed >>>= 8; + rSeed >>= 8; + } + } + } + + public void NextBytes( + byte[] bytes) + { + NextBytes(bytes, 0, bytes.Length); + } + + public void NextBytes( + byte[] bytes, + int start, + int len) + { + lock (this) + { + int stateOff = 0; + + DigestDoFinal(state); + + int end = start + len; + for (int i = start; i < end; ++i) + { + if (stateOff == state.Length) + { + DigestUpdate(counter++); + DigestUpdate(state); + DigestDoFinal(state); + stateOff = 0; + } + bytes[i] = state[stateOff++]; + } + + DigestUpdate(counter++); + DigestUpdate(state); + } + } + + private void DigestUpdate(long seed) + { + for (int i = 0; i != 8; i++) + { + digest.Update((byte)seed); +// seed >>>= 8; + 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/src/core/srcbc/crypto/prng/IRandomGenerator.cs b/src/core/srcbc/crypto/prng/IRandomGenerator.cs new file mode 100644 index 0000000..36c7dff --- /dev/null +++ b/src/core/srcbc/crypto/prng/IRandomGenerator.cs @@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Prng +{ + ///+ /// Access to internals is synchronized so a single one of these can be shared. + ///
+ ///+ * Based on an idea from Marcus Lippert. + *
+ */ + 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++) + { + while (this.counter == last) + { + try + { + Thread.Sleep(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. + *+ * 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. + *
+ * @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/src/core/srcbc/crypto/prng/VMPCRandomGenerator.cs b/src/core/srcbc/crypto/prng/VMPCRandomGenerator.cs new file mode 100644 index 0000000..d3a5a19 --- /dev/null +++ b/src/core/srcbc/crypto/prng/VMPCRandomGenerator.cs @@ -0,0 +1,115 @@ +namespace Org.BouncyCastle.Crypto.Prng +{ + public class VmpcRandomGenerator + : IRandomGenerator + { + private byte n = 0; + + ///
+ /// // 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;
+ /// }
+ /// + /// Note: the usual length for the salt is the length of the hash + /// function used in bytes.
+ ///+ /// Note: the usual value for the salt length is the number of + /// bytes in the hash function.
+ ///+ /// DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING. + ///+ ///
+ /// This file could be more optimized. + ///
+ ///From Knuth Vol 2, pg 395.
+ */ + public bool IsProbablePrime( + int certainty) + { + if (certainty <= 0) + return true; + + BigInteger n = Abs(); + + if (!n.TestBit(0)) + return n.Equals(Two); + + if (n.Equals(One)) + return false; + + return n.CheckProbablePrime(certainty, RandomSource); + } + + private bool CheckProbablePrime( + int certainty, + Random random) + { + Debug.Assert(certainty > 0); + Debug.Assert(CompareTo(Two) > 0); + Debug.Assert(TestBit(0)); + + + // Try to reduce the penalty for really small numbers + int numLists = System.Math.Min(BitLength - 1, primeLists.Length); + + for (int i = 0; i < numLists; ++i) + { + int test = Remainder(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) + { + // We may find small numbers in the list + return BitLength < 16 && IntValue == prime; + } + } + } + + + // TODO Special case for < 10^16 (RabinMiller fixed list) +// if (BitLength < 30) +// { +// RabinMiller against 2, 3, 5, 7, 11, 13, 23 is sufficient +// } + + + // TODO Is it worth trying to create a hybrid of these two? + return RabinMillerTest(certainty, random); +// return SolovayStrassenTest(certainty, random); + +// bool rbTest = RabinMillerTest(certainty, random); +// bool ssTest = SolovayStrassenTest(certainty, random); +// +// Debug.Assert(rbTest == ssTest); +// +// return rbTest; + } + + internal bool RabinMillerTest( + int certainty, + Random random) + { + Debug.Assert(certainty > 0); + Debug.Assert(BitLength > 2); + Debug.Assert(TestBit(0)); + + // let n = 1 + d . 2^s + BigInteger n = this; + BigInteger nMinusOne = n.Subtract(One); + int s = nMinusOne.GetLowestSetBit(); + BigInteger r = nMinusOne.ShiftRight(s); + + Debug.Assert(s >= 1); + + do + { + // TODO Make a method for random BigIntegers in range 0 < x < n) + // - Method can be optimized by only replacing examined bits at each trial + BigInteger a; + do + { + a = new BigInteger(n.BitLength, random); + } + while (a.CompareTo(One) <= 0 || a.CompareTo(nMinusOne) >= 0); + + BigInteger y = a.ModPow(r, n); + + if (!y.Equals(One)) + { + int j = 0; + while (!y.Equals(nMinusOne)) + { + if (++j == s) + return false; + + y = y.ModPow(Two, n); + + if (y.Equals(One)) + return false; + } + } + + certainty -= 2; // composites pass for only 1/4 possible 'a' + } + while (certainty > 0); + + return true; + } + +// private bool SolovayStrassenTest( +// int certainty, +// Random random) +// { +// Debug.Assert(certainty > 0); +// Debug.Assert(CompareTo(Two) > 0); +// Debug.Assert(TestBit(0)); +// +// BigInteger n = this; +// BigInteger nMinusOne = n.Subtract(One); +// BigInteger e = nMinusOne.ShiftRight(1); +// +// do +// { +// BigInteger a; +// do +// { +// a = new BigInteger(nBitLength, random); +// } +// // NB: Spec says 0 < x < n, but 1 is trivial +// while (a.CompareTo(One) <= 0 || a.CompareTo(n) >= 0); +// +// +// // TODO Check this is redundant given the way Jacobi() works? +//// if (!a.Gcd(n).Equals(One)) +//// return false; +// +// int x = Jacobi(a, n); +// +// if (x == 0) +// return false; +// +// BigInteger check = a.ModPow(e, n); +// +// if (x == 1 && !check.Equals(One)) +// return false; +// +// if (x == -1 && !check.Equals(nMinusOne)) +// return false; +// +// --certainty; +// } +// while (certainty > 0); +// +// return true; +// } +// +// private static int Jacobi( +// BigInteger a, +// BigInteger b) +// { +// Debug.Assert(a.sign >= 0); +// Debug.Assert(b.sign > 0); +// Debug.Assert(b.TestBit(0)); +// Debug.Assert(a.CompareTo(b) < 0); +// +// int totalS = 1; +// for (;;) +// { +// if (a.sign == 0) +// return 0; +// +// if (a.Equals(One)) +// break; +// +// int e = a.GetLowestSetBit(); +// +// int bLsw = b.magnitude[b.magnitude.Length - 1]; +// if ((e & 1) != 0 && ((bLsw & 7) == 3 || (bLsw & 7) == 5)) +// totalS = -totalS; +// +// // TODO Confirm this is faster than later a1.Equals(One) test +// if (a.BitLength == e + 1) +// break; +// BigInteger a1 = a.ShiftRight(e); +//// if (a1.Equals(One)) +//// break; +// +// int a1Lsw = a1.magnitude[a1.magnitude.Length - 1]; +// if ((bLsw & 3) == 3 && (a1Lsw & 3) == 3) +// totalS = -totalS; +// +//// a = b.Mod(a1); +// a = b.Remainder(a1); +// b = a1; +// } +// return totalS; +// } + + public long LongValue + { + get + { + if (sign == 0) + return 0; + + long v; + if (magnitude.Length > 1) + { + v = ((long)magnitude[magnitude.Length - 2] << 32) + | (magnitude[magnitude.Length - 1] & IMASK); + } + else + { + v = (magnitude[magnitude.Length - 1] & IMASK); + } + + return sign < 0 ? -v : v; + } + } + + public BigInteger Max( + BigInteger value) + { + return CompareTo(value) > 0 ? this : value; + } + + public BigInteger Min( + BigInteger value) + { + return CompareTo(value) < 0 ? this : value; + } + + public BigInteger Mod( + BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + BigInteger biggie = Remainder(m); + + return (biggie.sign >= 0 ? biggie : biggie.Add(m)); + } + + public BigInteger ModInverse( + BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + // TODO Too slow at the moment +// // "Fast Key Exchange with Elliptic Curve Systems" R.Schoeppel +// if (m.TestBit(0)) +// { +// //The Almost Inverse Algorithm +// int k = 0; +// BigInteger B = One, C = Zero, F = this, G = m, tmp; +// +// for (;;) +// { +// // While F is even, do F=F/u, C=C*u, k=k+1. +// int zeroes = F.GetLowestSetBit(); +// if (zeroes > 0) +// { +// F = F.ShiftRight(zeroes); +// C = C.ShiftLeft(zeroes); +// k += zeroes; +// } +// +// // If F = 1, then return B,k. +// if (F.Equals(One)) +// { +// BigInteger half = m.Add(One).ShiftRight(1); +// BigInteger halfK = half.ModPow(BigInteger.ValueOf(k), m); +// return B.Multiply(halfK).Mod(m); +// } +// +// if (F.CompareTo(G) < 0) +// { +// tmp = G; G = F; F = tmp; +// tmp = B; B = C; C = tmp; +// } +// +// F = F.Add(G); +// B = B.Add(C); +// } +// } + + BigInteger x = new BigInteger(); + BigInteger gcd = ExtEuclid(this.Mod(m), m, x, null); + + if (!gcd.Equals(One)) + throw new ArithmeticException("Numbers not relatively prime."); + + if (x.sign < 0) + { + x.sign = 1; + //x = m.Subtract(x); + x.magnitude = doSubBigLil(m.magnitude, x.magnitude); + } + + return x; + } + + /** + * Calculate the numbers u1, u2, and u3 such that: + * + * u1 * a + u2 * b = u3 + * + * where u3 is the greatest common divider of a and b. + * a and b using the extended Euclid algorithm (refer p. 323 + * of The Art of Computer Programming vol 2, 2nd ed). + * This also seems to have the side effect of calculating + * some form of multiplicative inverse. + * + * @param a First number to calculate gcd for + * @param b Second number to calculate gcd for + * @param u1Out the return object for the u1 value + * @param u2Out the return object for the u2 value + * @return The greatest common divisor of a and b + */ + private static BigInteger ExtEuclid( + BigInteger a, + BigInteger b, + BigInteger u1Out, + BigInteger u2Out) + { + BigInteger u1 = One; + BigInteger u3 = a; + BigInteger v1 = Zero; + BigInteger v3 = b; + + while (v3.sign > 0) + { + BigInteger[] q = u3.DivideAndRemainder(v3); + + BigInteger tmp = v1.Multiply(q[0]); + BigInteger tn = u1.Subtract(tmp); + u1 = v1; + v1 = tn; + + u3 = v3; + v3 = q[1]; + } + + if (u1Out != null) + { + u1Out.sign = u1.sign; + u1Out.magnitude = u1.magnitude; + } + + if (u2Out != null) + { + BigInteger tmp = u1.Multiply(a); + tmp = u3.Subtract(tmp); + BigInteger res = tmp.Divide(b); + u2Out.sign = res.sign; + u2Out.magnitude = res.magnitude; + } + + return u3; + } + + private static void ZeroOut( + int[] x) + { + Array.Clear(x, 0, x.Length); + } + + public BigInteger ModPow( + BigInteger exponent, + BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + if (m.Equals(One)) + return Zero; + + if (exponent.sign == 0) + return One; + + if (sign == 0) + return Zero; + + int[] zVal = null; + int[] yAccum = null; + int[] yVal; + + // Montgomery exponentiation is only possible if the modulus is odd, + // but AFAIK, this is always the case for crypto algo's + bool useMonty = ((m.magnitude[m.magnitude.Length - 1] & 1) == 1); + long mQ = 0; + if (useMonty) + { + mQ = m.GetMQuote(); + + // tmp = this * R mod m + BigInteger tmp = ShiftLeft(32 * m.magnitude.Length).Mod(m); + zVal = tmp.magnitude; + + useMonty = (zVal.Length <= m.magnitude.Length); + + if (useMonty) + { + yAccum = new int[m.magnitude.Length + 1]; + if (zVal.Length < m.magnitude.Length) + { + int[] longZ = new int[m.magnitude.Length]; + zVal.CopyTo(longZ, longZ.Length - zVal.Length); + zVal = longZ; + } + } + } + + if (!useMonty) + { + if (magnitude.Length <= m.magnitude.Length) + { + //zAccum = new int[m.magnitude.Length * 2]; + zVal = new int[m.magnitude.Length]; + magnitude.CopyTo(zVal, zVal.Length - magnitude.Length); + } + else + { + // + // in normal practice we'll never see this... + // + BigInteger tmp = Remainder(m); + + //zAccum = new int[m.magnitude.Length * 2]; + zVal = new int[m.magnitude.Length]; + tmp.magnitude.CopyTo(zVal, zVal.Length - tmp.magnitude.Length); + } + + yAccum = new int[m.magnitude.Length * 2]; + } + + yVal = new int[m.magnitude.Length]; + + // + // from LSW to MSW + // + for (int i = 0; i < exponent.magnitude.Length; i++) + { + int v = exponent.magnitude[i]; + int bits = 0; + + if (i == 0) + { + while (v > 0) + { + v <<= 1; + bits++; + } + + // + // first time in initialise y + // + zVal.CopyTo(yVal, 0); + + v <<= 1; + bits++; + } + + while (v != 0) + { + if (useMonty) + { + // Montgomery square algo doesn't exist, and a normal + // square followed by a Montgomery reduction proved to + // be almost as heavy as a Montgomery mulitply. + MultiplyMonty(yAccum, yVal, yVal, m.magnitude, mQ); + } + else + { + Square(yAccum, yVal); + Remainder(yAccum, m.magnitude); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); + ZeroOut(yAccum); + } + bits++; + + if (v < 0) + { + if (useMonty) + { + MultiplyMonty(yAccum, yVal, zVal, m.magnitude, mQ); + } + else + { + Multiply(yAccum, yVal, zVal); + Remainder(yAccum, m.magnitude); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, + yVal.Length); + ZeroOut(yAccum); + } + } + + v <<= 1; + } + + while (bits < 32) + { + if (useMonty) + { + MultiplyMonty(yAccum, yVal, yVal, m.magnitude, mQ); + } + else + { + Square(yAccum, yVal); + Remainder(yAccum, m.magnitude); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); + ZeroOut(yAccum); + } + bits++; + } + } + + if (useMonty) + { + // Return y * R^(-1) mod m by doing y * 1 * R^(-1) mod m + ZeroOut(zVal); + zVal[zVal.Length - 1] = 1; + MultiplyMonty(yAccum, yVal, zVal, m.magnitude, mQ); + } + + BigInteger result = new BigInteger(1, yVal, true); + + return exponent.sign > 0 + ? result + : result.ModInverse(m); + } + + /** + * return w with w = x * x - w is assumed to have enough space. + */ + private static int[] Square( + int[] w, + int[] x) + { + // Note: this method allows w to be only (2 * x.Length - 1) words if result will fit +// if (w.Length != 2 * x.Length) +// throw new ArgumentException("no I don't think so..."); + + ulong u1, u2, c; + + int wBase = w.Length - 1; + + for (int i = x.Length - 1; i != 0; i--) + { + ulong v = (ulong)(uint) x[i]; + + u1 = v * v; + u2 = u1 >> 32; + u1 = (uint) u1; + + u1 += (ulong)(uint) w[wBase]; + + w[wBase] = (int)(uint) u1; + c = u2 + (u1 >> 32); + + for (int j = i - 1; j >= 0; j--) + { + --wBase; + u1 = v * (ulong)(uint) x[j]; + u2 = u1 >> 31; // multiply by 2! + u1 = (uint)(u1 << 1); // multiply by 2! + u1 += c + (ulong)(uint) w[wBase]; + + w[wBase] = (int)(uint) u1; + c = u2 + (u1 >> 32); + } + + c += (ulong)(uint) w[--wBase]; + w[wBase] = (int)(uint) c; + + if (--wBase >= 0) + { + w[wBase] = (int)(uint)(c >> 32); + } + else + { + Debug.Assert((uint)(c >> 32) == 0); + } + wBase += i; + } + + u1 = (ulong)(uint) x[0]; + u1 = u1 * u1; + u2 = u1 >> 32; + u1 = u1 & IMASK; + + u1 += (ulong)(uint) w[wBase]; + + w[wBase] = (int)(uint) u1; + if (--wBase >= 0) + { + w[wBase] = (int)(uint)(u2 + (u1 >> 32) + (ulong)(uint) w[wBase]); + } + else + { + Debug.Assert((uint)(u2 + (u1 >> 32)) == 0); + } + + return w; + } + + /** + * return x with x = y * z - x is assumed to have enough space. + */ + private static int[] Multiply( + int[] x, + int[] y, + int[] z) + { + int i = z.Length; + + if (i < 1) + return x; + + int xBase = x.Length - y.Length; + + for (;;) + { + long a = z[--i] & IMASK; + long val = 0; + + for (int j = y.Length - 1; j >= 0; j--) + { + val += a * (y[j] & IMASK) + (x[xBase + j] & IMASK); + + x[xBase + j] = (int)val; + + val = (long)((ulong)val >> 32); + } + + --xBase; + + if (i < 1) + { + if (xBase >= 0) + { + x[xBase] = (int)val; + } + else + { + Debug.Assert(val == 0); + } + break; + } + + x[xBase] = (int)val; + } + + return x; + } + + private static long FastExtEuclid( + long a, + long b, + long[] uOut) + { + long u1 = 1; + long u3 = a; + long v1 = 0; + long v3 = b; + + while (v3 > 0) + { + long q, tn; + + q = u3 / v3; + + tn = u1 - (v1 * q); + u1 = v1; + v1 = tn; + + tn = u3 - (v3 * q); + u3 = v3; + v3 = tn; + } + + uOut[0] = u1; + uOut[1] = (u3 - (u1 * a)) / b; + + return u3; + } + + private static long FastModInverse( + long v, + long m) + { + if (m < 1) + throw new ArithmeticException("Modulus must be positive"); + + long[] x = new long[2]; + long gcd = FastExtEuclid(v, m, x); + + if (gcd != 1) + throw new ArithmeticException("Numbers not relatively prime."); + + if (x[0] < 0) + { + x[0] += m; + } + + return x[0]; + } + +// private static BigInteger MQuoteB = One.ShiftLeft(32); +// private static BigInteger MQuoteBSub1 = MQuoteB.Subtract(One); + + /** + * Calculate mQuote = -m^(-1) mod b with b = 2^32 (32 = word size) + */ + private long GetMQuote() + { + Debug.Assert(this.sign > 0); + + if (mQuote != -1) + { + return mQuote; // already calculated + } + + if (magnitude.Length == 0 || (magnitude[magnitude.Length - 1] & 1) == 0) + { + return -1; // not for even numbers + } + + long v = (((~this.magnitude[this.magnitude.Length - 1]) | 1) & 0xffffffffL); + mQuote = FastModInverse(v, 0x100000000L); + + return mQuote; + } + + /** + * Montgomery multiplication: a = x * y * R^(-1) mod m + *Fp
(X9.62 s 4.2.1 pg 17).
+ * @return The decoded point.
+ */
+ public override ECPoint DecodePoint(
+ byte[] encoded)
+ {
+ ECPoint p = null;
+ int expectedLength = (FieldSize + 7) / 8;
+
+ switch (encoded[0])
+ {
+ case 0x00: // infinity
+ {
+ if (encoded.Length != 1)
+ throw new ArgumentException("Incorrect length for infinity encoding", "encoded");
+
+ p = Infinity;
+ break;
+ }
+
+ case 0x02: // compressed
+ case 0x03: // compressed
+ {
+ if (encoded.Length != (expectedLength + 1))
+ throw new ArgumentException("Incorrect length for compressed encoding", "encoded");
+
+ int yTilde = encoded[0] & 1;
+ BigInteger X1 = new BigInteger(1, encoded, 1, encoded.Length - 1);
+
+ p = DecompressPoint(yTilde, X1);
+ break;
+ }
+
+ case 0x04: // uncompressed
+ case 0x06: // hybrid
+ case 0x07: // hybrid
+ {
+ if (encoded.Length != (2 * expectedLength + 1))
+ throw new ArgumentException("Incorrect length for uncompressed/hybrid encoding", "encoded");
+
+ BigInteger X1 = new BigInteger(1, encoded, 1, expectedLength);
+ BigInteger Y1 = new BigInteger(1, encoded, 1 + expectedLength, expectedLength);
+
+ p = CreatePoint(X1, Y1, false);
+ break;
+ }
+
+ default:
+ throw new FormatException("Invalid point encoding " + encoded[0]);
+ }
+
+ return p;
+ }
+ }
+
+ /**
+ * Elliptic curve over Fp
+ */
+ public class FpCurve : ECCurveBase
+ {
+ private readonly BigInteger q;
+ private readonly FpPoint infinity;
+
+ public FpCurve(BigInteger q, BigInteger a, BigInteger b)
+ {
+ this.q = q;
+ this.a = FromBigInteger(a);
+ this.b = FromBigInteger(b);
+ this.infinity = new FpPoint(this, null, null);
+ }
+
+ public BigInteger Q
+ {
+ get { return q; }
+ }
+
+ public override ECPoint Infinity
+ {
+ get { return infinity; }
+ }
+
+ public override int FieldSize
+ {
+ get { return q.BitLength; }
+ }
+
+ public override ECFieldElement FromBigInteger(BigInteger x)
+ {
+ return new FpFieldElement(this.q, x);
+ }
+
+ public override ECPoint CreatePoint(
+ BigInteger X1,
+ BigInteger Y1,
+ bool withCompression)
+ {
+ // TODO Validation of X1, Y1?
+ return new FpPoint(
+ this,
+ FromBigInteger(X1),
+ FromBigInteger(Y1),
+ withCompression);
+ }
+
+ protected internal override ECPoint DecompressPoint(
+ int yTilde,
+ BigInteger X1)
+ {
+ ECFieldElement x = FromBigInteger(X1);
+ ECFieldElement alpha = x.Multiply(x.Square().Add(a)).Add(b);
+ ECFieldElement beta = alpha.Sqrt();
+
+ //
+ // if we can't find a sqrt we haven't got a point on the
+ // curve - run!
+ //
+ if (beta == null)
+ throw new ArithmeticException("Invalid point compression");
+
+ BigInteger betaValue = beta.ToBigInteger();
+ int bit0 = betaValue.TestBit(0) ? 1 : 0;
+
+ if (bit0 != yTilde)
+ {
+ // Use the other root
+ beta = FromBigInteger(q.Subtract(betaValue));
+ }
+
+ return new FpPoint(this, x, beta, true);
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ FpCurve other = obj as FpCurve;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ FpCurve other)
+ {
+ return base.Equals(other) && q.Equals(other.q);
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode() ^ q.GetHashCode();
+ }
+ }
+
+ /**
+ * Elliptic curves over F2m. The Weierstrass equation is given by
+ * y2 + xy = x3 + ax2 + b
.
+ */
+ public class F2mCurve : ECCurveBase
+ {
+ /**
+ * The exponent m
of F2m
.
+ */
+ private readonly int m;
+
+ /**
+ * TPB: The integer k
where xm +
+ * xk + 1
represents the reduction polynomial
+ * f(z)
.k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.μ
of the elliptic curve if this is
+ * a Koblitz curve.
+ */
+ private sbyte mu = 0;
+
+ /**
+ * The auxiliary values s0
and
+ * s1
used for partial modular reduction for
+ * Koblitz curves.
+ */
+ private BigInteger[] si = null;
+
+ /**
+ * Constructor for Trinomial Polynomial Basis (TPB).
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k The integer k
where xm +
+ * xk + 1
represents the reduction
+ * polynomial f(z)
.
+ * @param a The coefficient a
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param b The coefficient b
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ */
+ public F2mCurve(
+ int m,
+ int k,
+ BigInteger a,
+ BigInteger b)
+ : this(m, k, 0, 0, a, b, null, null)
+ {
+ }
+
+ /**
+ * Constructor for Trinomial Polynomial Basis (TPB).
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k The integer k
where xm +
+ * xk + 1
represents the reduction
+ * polynomial f(z)
.
+ * @param a The coefficient a
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param b The coefficient b
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param n The order of the main subgroup of the elliptic curve.
+ * @param h The cofactor of the elliptic curve, i.e.
+ * #Ea(F2m) = h * n
.
+ */
+ public F2mCurve(
+ int m,
+ int k,
+ BigInteger a,
+ BigInteger b,
+ BigInteger n,
+ BigInteger h)
+ : this(m, k, 0, 0, a, b, n, h)
+ {
+ }
+
+ /**
+ * Constructor for Pentanomial Polynomial Basis (PPB).
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k1 The integer k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k2 The integer k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k3 The integer k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param a The coefficient a
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param b The coefficient b
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ */
+ public F2mCurve(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger a,
+ BigInteger b)
+ : this(m, k1, k2, k3, a, b, null, null)
+ {
+ }
+
+ /**
+ * Constructor for Pentanomial Polynomial Basis (PPB).
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k1 The integer k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k2 The integer k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k3 The integer k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param a The coefficient a
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param b The coefficient b
in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * F2m
.
+ * @param n The order of the main subgroup of the elliptic curve.
+ * @param h The cofactor of the elliptic curve, i.e.
+ * #Ea(F2m) = h * n
.
+ */
+ public F2mCurve(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger a,
+ BigInteger b,
+ BigInteger n,
+ BigInteger h)
+ {
+ this.m = m;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+ this.n = n;
+ this.h = h;
+ this.infinity = new F2mPoint(this, null, null);
+
+ if (k1 == 0)
+ throw new ArgumentException("k1 must be > 0");
+
+ if (k2 == 0)
+ {
+ if (k3 != 0)
+ throw new ArgumentException("k3 must be 0 if k2 == 0");
+ }
+ else
+ {
+ if (k2 <= k1)
+ throw new ArgumentException("k2 must be > k1");
+
+ if (k3 <= k2)
+ throw new ArgumentException("k3 must be > k2");
+ }
+
+ this.a = FromBigInteger(a);
+ this.b = FromBigInteger(b);
+ }
+
+ public override ECPoint Infinity
+ {
+ get { return infinity; }
+ }
+
+ public override int FieldSize
+ {
+ get { return m; }
+ }
+
+ public override ECFieldElement FromBigInteger(BigInteger x)
+ {
+ return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, x);
+ }
+
+ /**
+ * Returns true if this is a Koblitz curve (ABC curve).
+ * @return true if this is a Koblitz curve (ABC curve), false otherwise
+ */
+ public bool IsKoblitz
+ {
+ get
+ {
+ return n != null && h != null
+ && (a.ToBigInteger().Equals(BigInteger.Zero)
+ || a.ToBigInteger().Equals(BigInteger.One))
+ && b.ToBigInteger().Equals(BigInteger.One);
+ }
+ }
+
+ /**
+ * Returns the parameter μ
of the elliptic curve.
+ * @return μ
of the elliptic curve.
+ * @throws ArgumentException if the given ECCurve is not a
+ * Koblitz curve.
+ */
+ internal sbyte GetMu()
+ {
+ if (mu == 0)
+ {
+ lock (this)
+ {
+ if (mu == 0)
+ {
+ mu = Tnaf.GetMu(this);
+ }
+ }
+ }
+
+ return mu;
+ }
+
+ /**
+ * @return the auxiliary values s0
and
+ * s1
used for partial modular reduction for
+ * Koblitz curves.
+ */
+ internal BigInteger[] GetSi()
+ {
+ if (si == null)
+ {
+ lock (this)
+ {
+ if (si == null)
+ {
+ si = Tnaf.GetSi(this);
+ }
+ }
+ }
+ return si;
+ }
+
+ public override ECPoint CreatePoint(
+ BigInteger X1,
+ BigInteger Y1,
+ bool withCompression)
+ {
+ // TODO Validation of X1, Y1?
+ return new F2mPoint(
+ this,
+ FromBigInteger(X1),
+ FromBigInteger(Y1),
+ withCompression);
+ }
+
+ protected internal override ECPoint DecompressPoint(
+ int yTilde,
+ BigInteger X1)
+ {
+ ECFieldElement xp = FromBigInteger(X1);
+ ECFieldElement yp = null;
+ if (xp.ToBigInteger().SignValue == 0)
+ {
+ yp = (F2mFieldElement)b;
+ for (int i = 0; i < m - 1; i++)
+ {
+ yp = yp.Square();
+ }
+ }
+ else
+ {
+ ECFieldElement beta = xp.Add(a).Add(
+ b.Multiply(xp.Square().Invert()));
+ ECFieldElement z = solveQuadradicEquation(beta);
+
+ if (z == null)
+ throw new ArithmeticException("Invalid point compression");
+
+ int zBit = z.ToBigInteger().TestBit(0) ? 1 : 0;
+ if (zBit != yTilde)
+ {
+ z = z.Add(FromBigInteger(BigInteger.One));
+ }
+
+ yp = xp.Multiply(z);
+ }
+
+ return new F2mPoint(this, xp, yp, true);
+ }
+
+ /**
+ * Solves a quadratic equation z2 + z = beta
(X9.62
+ * D.1.6) The other solution is z + 1
.
+ *
+ * @param beta
+ * The value to solve the qradratic equation for.
+ * @return the solution for z2 + z = beta
or
+ * null
if no solution exists.
+ */
+ private ECFieldElement solveQuadradicEquation(ECFieldElement beta)
+ {
+ if (beta.ToBigInteger().SignValue == 0)
+ {
+ return FromBigInteger(BigInteger.Zero);
+ }
+
+ ECFieldElement z = null;
+ ECFieldElement gamma = FromBigInteger(BigInteger.Zero);
+
+ while (gamma.ToBigInteger().SignValue == 0)
+ {
+ ECFieldElement t = FromBigInteger(new BigInteger(m, new Random()));
+ z = FromBigInteger(BigInteger.Zero);
+
+ ECFieldElement w = beta;
+ for (int i = 1; i <= m - 1; i++)
+ {
+ ECFieldElement w2 = w.Square();
+ z = z.Square().Add(w2.Multiply(t));
+ w = w2.Add(beta);
+ }
+ if (w.ToBigInteger().SignValue != 0)
+ {
+ return null;
+ }
+ gamma = z.Square().Add(z);
+ }
+ return z;
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ F2mCurve other = obj as F2mCurve;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ F2mCurve other)
+ {
+ return m == other.m
+ && k1 == other.k1
+ && k2 == other.k2
+ && k3 == other.k3
+ && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode() ^ m ^ k1 ^ k2 ^ k3;
+ }
+
+ public int M
+ {
+ get { return m; }
+ }
+
+ /**
+ * Return true if curve uses a Trinomial basis.
+ *
+ * @return true if curve Trinomial, false otherwise.
+ */
+ public bool IsTrinomial()
+ {
+ return k2 == 0 && k3 == 0;
+ }
+
+ public int K1
+ {
+ get { return k1; }
+ }
+
+ public int K2
+ {
+ get { return k2; }
+ }
+
+ public int K3
+ {
+ get { return k3; }
+ }
+
+ public BigInteger N
+ {
+ get { return n; }
+ }
+
+ public BigInteger H
+ {
+ get { return h; }
+ }
+ }
+}
diff --git a/src/core/srcbc/math/ec/ECFieldElement.cs b/src/core/srcbc/math/ec/ECFieldElement.cs
new file mode 100644
index 0000000..7a30c14
--- /dev/null
+++ b/src/core/srcbc/math/ec/ECFieldElement.cs
@@ -0,0 +1,1253 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC
+{
+ public abstract class ECFieldElement
+ {
+ public abstract BigInteger ToBigInteger();
+ public abstract string FieldName { get; }
+ public abstract int FieldSize { get; }
+ public abstract ECFieldElement Add(ECFieldElement b);
+ public abstract ECFieldElement Subtract(ECFieldElement b);
+ public abstract ECFieldElement Multiply(ECFieldElement b);
+ public abstract ECFieldElement Divide(ECFieldElement b);
+ public abstract ECFieldElement Negate();
+ public abstract ECFieldElement Square();
+ public abstract ECFieldElement Invert();
+ public abstract ECFieldElement Sqrt();
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ ECFieldElement other = obj as ECFieldElement;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ ECFieldElement other)
+ {
+ return ToBigInteger().Equals(other.ToBigInteger());
+ }
+
+ public override int GetHashCode()
+ {
+ return ToBigInteger().GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return this.ToBigInteger().ToString(2);
+ }
+ }
+
+ public class FpFieldElement
+ : ECFieldElement
+ {
+ private readonly BigInteger q, x;
+
+ public FpFieldElement(
+ BigInteger q,
+ BigInteger x)
+ {
+ if (x.CompareTo(q) >= 0)
+ throw new ArgumentException("x value too large in field element");
+
+ this.q = q;
+ this.x = x;
+ }
+
+ public override BigInteger ToBigInteger()
+ {
+ return x;
+ }
+
+ /**
+ * return the field name for this field.
+ *
+ * @return the string "Fp".
+ */
+ public override string FieldName
+ {
+ get { return "Fp"; }
+ }
+
+ public override int FieldSize
+ {
+ get { return q.BitLength; }
+ }
+
+ public BigInteger Q
+ {
+ get { return q; }
+ }
+
+ public override ECFieldElement Add(
+ ECFieldElement b)
+ {
+ return new FpFieldElement(q, x.Add(b.ToBigInteger()).Mod(q));
+ }
+
+ public override ECFieldElement Subtract(
+ ECFieldElement b)
+ {
+ return new FpFieldElement(q, x.Subtract(b.ToBigInteger()).Mod(q));
+ }
+
+ public override ECFieldElement Multiply(
+ ECFieldElement b)
+ {
+ return new FpFieldElement(q, x.Multiply(b.ToBigInteger()).Mod(q));
+ }
+
+ public override ECFieldElement Divide(
+ ECFieldElement b)
+ {
+ return new FpFieldElement(q, x.Multiply(b.ToBigInteger().ModInverse(q)).Mod(q));
+ }
+
+ public override ECFieldElement Negate()
+ {
+ return new FpFieldElement(q, x.Negate().Mod(q));
+ }
+
+ public override ECFieldElement Square()
+ {
+ return new FpFieldElement(q, x.Multiply(x).Mod(q));
+ }
+
+ public override ECFieldElement Invert()
+ {
+ return new FpFieldElement(q, x.ModInverse(q));
+ }
+
+ // D.1.4 91
+ /**
+ * return a sqrt root - the routine verifies that the calculation
+ * returns the right value - if none exists it returns null.
+ */
+ public override ECFieldElement Sqrt()
+ {
+ if (!q.TestBit(0))
+ throw Platform.CreateNotImplementedException("even value of q");
+
+ // p mod 4 == 3
+ if (q.TestBit(1))
+ {
+ // TODO Can this be optimised (inline the Square?)
+ // z = g^(u+1) + p, p = 4u + 3
+ ECFieldElement z = new FpFieldElement(q, x.ModPow(q.ShiftRight(2).Add(BigInteger.One), q));
+
+ return z.Square().Equals(this) ? z : null;
+ }
+
+ // p mod 4 == 1
+ BigInteger qMinusOne = q.Subtract(BigInteger.One);
+
+ BigInteger legendreExponent = qMinusOne.ShiftRight(1);
+ if (!(x.ModPow(legendreExponent, q).Equals(BigInteger.One)))
+ return null;
+
+ BigInteger u = qMinusOne.ShiftRight(2);
+ BigInteger k = u.ShiftLeft(1).Add(BigInteger.One);
+
+ BigInteger Q = this.x;
+ BigInteger fourQ = Q.ShiftLeft(2).Mod(q);
+
+ BigInteger U, V;
+ do
+ {
+ Random rand = new Random();
+ BigInteger P;
+ do
+ {
+ P = new BigInteger(q.BitLength, rand);
+ }
+ while (P.CompareTo(q) >= 0
+ || !(P.Multiply(P).Subtract(fourQ).ModPow(legendreExponent, q).Equals(qMinusOne)));
+
+ BigInteger[] result = fastLucasSequence(q, P, Q, k);
+ U = result[0];
+ V = result[1];
+
+ if (V.Multiply(V).Mod(q).Equals(fourQ))
+ {
+ // Integer division by 2, mod q
+ if (V.TestBit(0))
+ {
+ V = V.Add(q);
+ }
+
+ V = V.ShiftRight(1);
+
+ Debug.Assert(V.Multiply(V).Mod(q).Equals(x));
+
+ return new FpFieldElement(q, V);
+ }
+ }
+ while (U.Equals(BigInteger.One) || U.Equals(qMinusOne));
+
+ return null;
+
+
+// BigInteger qMinusOne = q.Subtract(BigInteger.One);
+//
+// BigInteger legendreExponent = qMinusOne.ShiftRight(1);
+// if (!(x.ModPow(legendreExponent, q).Equals(BigInteger.One)))
+// return null;
+//
+// Random rand = new Random();
+// BigInteger fourX = x.ShiftLeft(2);
+//
+// BigInteger r;
+// do
+// {
+// r = new BigInteger(q.BitLength, rand);
+// }
+// while (r.CompareTo(q) >= 0
+// || !(r.Multiply(r).Subtract(fourX).ModPow(legendreExponent, q).Equals(qMinusOne)));
+//
+// BigInteger n1 = qMinusOne.ShiftRight(2);
+// BigInteger n2 = n1.Add(BigInteger.One);
+//
+// BigInteger wOne = WOne(r, x, q);
+// BigInteger wSum = W(n1, wOne, q).Add(W(n2, wOne, q)).Mod(q);
+// BigInteger twoR = r.ShiftLeft(1);
+//
+// BigInteger root = twoR.ModPow(q.Subtract(BigInteger.Two), q)
+// .Multiply(x).Mod(q)
+// .Multiply(wSum).Mod(q);
+//
+// return new FpFieldElement(q, root);
+ }
+
+// private static BigInteger W(BigInteger n, BigInteger wOne, BigInteger p)
+// {
+// if (n.Equals(BigInteger.One))
+// return wOne;
+//
+// bool isEven = !n.TestBit(0);
+// n = n.ShiftRight(1);
+// if (isEven)
+// {
+// BigInteger w = W(n, wOne, p);
+// return w.Multiply(w).Subtract(BigInteger.Two).Mod(p);
+// }
+// BigInteger w1 = W(n.Add(BigInteger.One), wOne, p);
+// BigInteger w2 = W(n, wOne, p);
+// return w1.Multiply(w2).Subtract(wOne).Mod(p);
+// }
+//
+// private BigInteger WOne(BigInteger r, BigInteger x, BigInteger p)
+// {
+// return r.Multiply(r).Multiply(x.ModPow(q.Subtract(BigInteger.Two), q)).Subtract(BigInteger.Two).Mod(p);
+// }
+
+ private static BigInteger[] fastLucasSequence(
+ BigInteger p,
+ BigInteger P,
+ BigInteger Q,
+ BigInteger k)
+ {
+ // TODO Research and apply "common-multiplicand multiplication here"
+
+ int n = k.BitLength;
+ int s = k.GetLowestSetBit();
+
+ Debug.Assert(k.TestBit(s));
+
+ BigInteger Uh = BigInteger.One;
+ BigInteger Vl = BigInteger.Two;
+ BigInteger Vh = P;
+ BigInteger Ql = BigInteger.One;
+ BigInteger Qh = BigInteger.One;
+
+ for (int j = n - 1; j >= s + 1; --j)
+ {
+ Ql = Ql.Multiply(Qh).Mod(p);
+
+ if (k.TestBit(j))
+ {
+ Qh = Ql.Multiply(Q).Mod(p);
+ Uh = Uh.Multiply(Vh).Mod(p);
+ Vl = Vh.Multiply(Vl).Subtract(P.Multiply(Ql)).Mod(p);
+ Vh = Vh.Multiply(Vh).Subtract(Qh.ShiftLeft(1)).Mod(p);
+ }
+ else
+ {
+ Qh = Ql;
+ Uh = Uh.Multiply(Vl).Subtract(Ql).Mod(p);
+ Vh = Vh.Multiply(Vl).Subtract(P.Multiply(Ql)).Mod(p);
+ Vl = Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)).Mod(p);
+ }
+ }
+
+ Ql = Ql.Multiply(Qh).Mod(p);
+ Qh = Ql.Multiply(Q).Mod(p);
+ Uh = Uh.Multiply(Vl).Subtract(Ql).Mod(p);
+ Vl = Vh.Multiply(Vl).Subtract(P.Multiply(Ql)).Mod(p);
+ Ql = Ql.Multiply(Qh).Mod(p);
+
+ for (int j = 1; j <= s; ++j)
+ {
+ Uh = Uh.Multiply(Vl).Mod(p);
+ Vl = Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)).Mod(p);
+ Ql = Ql.Multiply(Ql).Mod(p);
+ }
+
+ return new BigInteger[]{ Uh, Vl };
+ }
+
+// private static BigInteger[] verifyLucasSequence(
+// BigInteger p,
+// BigInteger P,
+// BigInteger Q,
+// BigInteger k)
+// {
+// BigInteger[] actual = fastLucasSequence(p, P, Q, k);
+// BigInteger[] plus1 = fastLucasSequence(p, P, Q, k.Add(BigInteger.One));
+// BigInteger[] plus2 = fastLucasSequence(p, P, Q, k.Add(BigInteger.Two));
+//
+// BigInteger[] check = stepLucasSequence(p, P, Q, actual, plus1);
+//
+// Debug.Assert(check[0].Equals(plus2[0]));
+// Debug.Assert(check[1].Equals(plus2[1]));
+//
+// return actual;
+// }
+//
+// private static BigInteger[] stepLucasSequence(
+// BigInteger p,
+// BigInteger P,
+// BigInteger Q,
+// BigInteger[] backTwo,
+// BigInteger[] backOne)
+// {
+// return new BigInteger[]
+// {
+// P.Multiply(backOne[0]).Subtract(Q.Multiply(backTwo[0])).Mod(p),
+// P.Multiply(backOne[1]).Subtract(Q.Multiply(backTwo[1])).Mod(p)
+// };
+// }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ FpFieldElement other = obj as FpFieldElement;
+
+ if (other == null)
+ return false;
+
+ return Equals(other);
+ }
+
+ protected bool Equals(
+ FpFieldElement other)
+ {
+ return q.Equals(other.q) && base.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return q.GetHashCode() ^ base.GetHashCode();
+ }
+ }
+
+// /**
+// * Class representing the Elements of the finite field
+// * F2m
in polynomial basis (PB)
+// * representation. Both trinomial (Tpb) and pentanomial (Ppb) polynomial
+// * basis representations are supported. Gaussian normal basis (GNB)
+// * representation is not supported.
+// */
+// public class F2mFieldElement
+// : ECFieldElement
+// {
+// /**
+// * Indicates gaussian normal basis representation (GNB). Number chosen
+// * according to X9.62. GNB is not implemented at present.
+// */
+// public const int Gnb = 1;
+//
+// /**
+// * Indicates trinomial basis representation (Tpb). Number chosen
+// * according to X9.62.
+// */
+// public const int Tpb = 2;
+//
+// /**
+// * Indicates pentanomial basis representation (Ppb). Number chosen
+// * according to X9.62.
+// */
+// public const int Ppb = 3;
+//
+// /**
+// * Tpb or Ppb.
+// */
+// private int representation;
+//
+// /**
+// * The exponent m
of F2m
.
+// */
+// private int m;
+//
+// /**
+// * Tpb: The integer k
where xm +
+// * xk + 1
represents the reduction polynomial
+// * f(z)
.k1
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k2
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k3
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.m
of
+// * F2m
.
+// * @param k1 The integer k1
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.
+// * @param k2 The integer k2
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.
+// * @param k3 The integer k3
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.
+// * @param x The BigInteger representing the value of the field element.
+// */
+// public F2mFieldElement(
+// int m,
+// int k1,
+// int k2,
+// int k3,
+// BigInteger x)
+// : base(x)
+// {
+// if ((k2 == 0) && (k3 == 0))
+// {
+// this.representation = Tpb;
+// }
+// else
+// {
+// if (k2 >= k3)
+// throw new ArgumentException("k2 must be smaller than k3");
+// if (k2 <= 0)
+// throw new ArgumentException("k2 must be larger than 0");
+//
+// this.representation = Ppb;
+// }
+//
+// if (x.SignValue < 0)
+// throw new ArgumentException("x value cannot be negative");
+//
+// this.m = m;
+// this.k1 = k1;
+// this.k2 = k2;
+// this.k3 = k3;
+// }
+//
+// /**
+// * Constructor for Tpb.
+// * @param m The exponent m
of
+// * F2m
.
+// * @param k The integer k
where xm +
+// * xk + 1
represents the reduction
+// * polynomial f(z)
.
+// * @param x The BigInteger representing the value of the field element.
+// */
+// public F2mFieldElement(
+// int m,
+// int k,
+// BigInteger x)
+// : this(m, k, 0, 0, x)
+// {
+// // Set k1 to k, and set k2 and k3 to 0
+// }
+//
+// public override string FieldName
+// {
+// get { return "F2m"; }
+// }
+//
+// /**
+// * Checks, if the ECFieldElements a
and b
+// * are elements of the same field F2m
+// * (having the same representation).
+// * @param a field element.
+// * @param b field element to be compared.
+// * @throws ArgumentException if a
and b
+// * are not elements of the same field
+// * F2m
(having the same
+// * representation).
+// */
+// public static void CheckFieldElements(
+// ECFieldElement a,
+// ECFieldElement b)
+// {
+// if (!(a is F2mFieldElement) || !(b is F2mFieldElement))
+// {
+// throw new ArgumentException("Field elements are not "
+// + "both instances of F2mFieldElement");
+// }
+//
+// if ((a.x.SignValue < 0) || (b.x.SignValue < 0))
+// {
+// throw new ArgumentException(
+// "x value may not be negative");
+// }
+//
+// F2mFieldElement aF2m = (F2mFieldElement)a;
+// F2mFieldElement bF2m = (F2mFieldElement)b;
+//
+// if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1)
+// || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3))
+// {
+// throw new ArgumentException("Field elements are not "
+// + "elements of the same field F2m");
+// }
+//
+// if (aF2m.representation != bF2m.representation)
+// {
+// // Should never occur
+// throw new ArgumentException(
+// "One of the field "
+// + "elements are not elements has incorrect representation");
+// }
+// }
+//
+// /**
+// * Computes z * a(z) mod f(z)
, where f(z)
is
+// * the reduction polynomial of this
.
+// * @param a The polynomial a(z)
to be multiplied by
+// * z mod f(z)
.
+// * @return z * a(z) mod f(z)
+// */
+// private BigInteger multZModF(
+// BigInteger a)
+// {
+// // Left-shift of a(z)
+// BigInteger az = a.ShiftLeft(1);
+// if (az.TestBit(this.m))
+// {
+// // If the coefficient of z^m in a(z) Equals 1, reduction
+// // modulo f(z) is performed: Add f(z) to to a(z):
+// // Step 1: Unset mth coeffient of a(z)
+// az = az.ClearBit(this.m);
+//
+// // Step 2: Add r(z) to a(z), where r(z) is defined as
+// // f(z) = z^m + r(z), and k1, k2, k3 are the positions of
+// // the non-zero coefficients in r(z)
+// az = az.FlipBit(0);
+// az = az.FlipBit(this.k1);
+// if (this.representation == Ppb)
+// {
+// az = az.FlipBit(this.k2);
+// az = az.FlipBit(this.k3);
+// }
+// }
+// return az;
+// }
+//
+// public override ECFieldElement Add(
+// ECFieldElement b)
+// {
+// // No check performed here for performance reasons. Instead the
+// // elements involved are checked in ECPoint.F2m
+// // checkFieldElements(this, b);
+// if (b.x.SignValue == 0)
+// return this;
+//
+// return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, this.x.Xor(b.x));
+// }
+//
+// public override ECFieldElement Subtract(
+// ECFieldElement b)
+// {
+// // Addition and subtraction are the same in F2m
+// return Add(b);
+// }
+//
+// public override ECFieldElement Multiply(
+// ECFieldElement b)
+// {
+// // Left-to-right shift-and-add field multiplication in F2m
+// // Input: Binary polynomials a(z) and b(z) of degree at most m-1
+// // Output: c(z) = a(z) * b(z) mod f(z)
+//
+// // No check performed here for performance reasons. Instead the
+// // elements involved are checked in ECPoint.F2m
+// // checkFieldElements(this, b);
+// BigInteger az = this.x;
+// BigInteger bz = b.x;
+// BigInteger cz;
+//
+// // Compute c(z) = a(z) * b(z) mod f(z)
+// if (az.TestBit(0))
+// {
+// cz = bz;
+// }
+// else
+// {
+// cz = BigInteger.Zero;
+// }
+//
+// for (int i = 1; i < this.m; i++)
+// {
+// // b(z) := z * b(z) mod f(z)
+// bz = multZModF(bz);
+//
+// if (az.TestBit(i))
+// {
+// // If the coefficient of x^i in a(z) Equals 1, b(z) is added
+// // to c(z)
+// cz = cz.Xor(bz);
+// }
+// }
+// return new F2mFieldElement(m, this.k1, this.k2, this.k3, cz);
+// }
+//
+//
+// public override ECFieldElement Divide(
+// ECFieldElement b)
+// {
+// // There may be more efficient implementations
+// ECFieldElement bInv = b.Invert();
+// return Multiply(bInv);
+// }
+//
+// public override ECFieldElement Negate()
+// {
+// // -x == x holds for all x in F2m
+// return this;
+// }
+//
+// public override ECFieldElement Square()
+// {
+// // Naive implementation, can probably be speeded up using modular
+// // reduction
+// return Multiply(this);
+// }
+//
+// public override ECFieldElement Invert()
+// {
+// // Inversion in F2m using the extended Euclidean algorithm
+// // Input: A nonzero polynomial a(z) of degree at most m-1
+// // Output: a(z)^(-1) mod f(z)
+//
+// // u(z) := a(z)
+// BigInteger uz = this.x;
+// if (uz.SignValue <= 0)
+// {
+// throw new ArithmeticException("x is zero or negative, " +
+// "inversion is impossible");
+// }
+//
+// // v(z) := f(z)
+// BigInteger vz = BigInteger.One.ShiftLeft(m);
+// vz = vz.SetBit(0);
+// vz = vz.SetBit(this.k1);
+// if (this.representation == Ppb)
+// {
+// vz = vz.SetBit(this.k2);
+// vz = vz.SetBit(this.k3);
+// }
+//
+// // g1(z) := 1, g2(z) := 0
+// BigInteger g1z = BigInteger.One;
+// BigInteger g2z = BigInteger.Zero;
+//
+// // while u != 1
+// while (uz.SignValue != 0)
+// {
+// // j := deg(u(z)) - deg(v(z))
+// int j = uz.BitLength - vz.BitLength;
+//
+// // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j
+// if (j < 0)
+// {
+// BigInteger uzCopy = uz;
+// uz = vz;
+// vz = uzCopy;
+//
+// BigInteger g1zCopy = g1z;
+// g1z = g2z;
+// g2z = g1zCopy;
+//
+// j = -j;
+// }
+//
+// // u(z) := u(z) + z^j * v(z)
+// // Note, that no reduction modulo f(z) is required, because
+// // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z)))
+// // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z))
+// // = deg(u(z))
+// uz = uz.Xor(vz.ShiftLeft(j));
+//
+// // g1(z) := g1(z) + z^j * g2(z)
+// g1z = g1z.Xor(g2z.ShiftLeft(j));
+// // if (g1z.BitLength() > this.m) {
+// // throw new ArithmeticException(
+// // "deg(g1z) >= m, g1z = " + g1z.ToString(2));
+// // }
+// }
+// return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, g2z);
+// }
+//
+// public override ECFieldElement Sqrt()
+// {
+// throw new ArithmeticException("Not implemented");
+// }
+//
+// /**
+// * @return the representation of the field
+// * F2m
, either of
+// * {@link F2mFieldElement.Tpb} (trinomial
+// * basis representation) or
+// * {@link F2mFieldElement.Ppb} (pentanomial
+// * basis representation).
+// */
+// public int Representation
+// {
+// get { return this.representation; }
+// }
+//
+// /**
+// * @return the degree m
of the reduction polynomial
+// * f(z)
.
+// */
+// public int M
+// {
+// get { return this.m; }
+// }
+//
+// /**
+// * @return Tpb: The integer k
where xm +
+// * xk + 1
represents the reduction polynomial
+// * f(z)
.k1
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k2
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.0
k3
where xm +
+// * xk3 + xk2 + xk1 + 1
+// * represents the reduction polynomial f(z)
.F2m
in polynomial basis (PB)
+ * representation. Both trinomial (Tpb) and pentanomial (Ppb) polynomial
+ * basis representations are supported. Gaussian normal basis (GNB)
+ * representation is not supported.
+ */
+ public class F2mFieldElement
+ : ECFieldElement
+ {
+ /**
+ * Indicates gaussian normal basis representation (GNB). Number chosen
+ * according to X9.62. GNB is not implemented at present.
+ */
+ public const int Gnb = 1;
+
+ /**
+ * Indicates trinomial basis representation (Tpb). Number chosen
+ * according to X9.62.
+ */
+ public const int Tpb = 2;
+
+ /**
+ * Indicates pentanomial basis representation (Ppb). Number chosen
+ * according to X9.62.
+ */
+ public const int Ppb = 3;
+
+ /**
+ * Tpb or Ppb.
+ */
+ private int representation;
+
+ /**
+ * The exponent m
of F2m
.
+ */
+ private int m;
+
+ /**
+ * Tpb: The integer k
where xm +
+ * xk + 1
represents the reduction polynomial
+ * f(z)
.k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.IntArray
holding the bits.
+ */
+ private IntArray x;
+
+ /**
+ * The number of int
s required to hold m
bits.
+ */
+ private readonly int t;
+
+ /**
+ * Constructor for Ppb.
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k1 The integer k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k2 The integer k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param k3 The integer k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.
+ * @param x The BigInteger representing the value of the field element.
+ */
+ public F2mFieldElement(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger x)
+ {
+ // t = m / 32 rounded up to the next integer
+ this.t = (m + 31) >> 5;
+ this.x = new IntArray(x, t);
+
+ if ((k2 == 0) && (k3 == 0))
+ {
+ this.representation = Tpb;
+ }
+ else
+ {
+ if (k2 >= k3)
+ throw new ArgumentException("k2 must be smaller than k3");
+ if (k2 <= 0)
+ throw new ArgumentException("k2 must be larger than 0");
+
+ this.representation = Ppb;
+ }
+
+ if (x.SignValue < 0)
+ throw new ArgumentException("x value cannot be negative");
+
+ this.m = m;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+ }
+
+ /**
+ * Constructor for Tpb.
+ * @param m The exponent m
of
+ * F2m
.
+ * @param k The integer k
where xm +
+ * xk + 1
represents the reduction
+ * polynomial f(z)
.
+ * @param x The BigInteger representing the value of the field element.
+ */
+ public F2mFieldElement(
+ int m,
+ int k,
+ BigInteger x)
+ : this(m, k, 0, 0, x)
+ {
+ // Set k1 to k, and set k2 and k3 to 0
+ }
+
+ private F2mFieldElement(int m, int k1, int k2, int k3, IntArray x)
+ {
+ t = (m + 31) >> 5;
+ this.x = x;
+ this.m = m;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+
+ if ((k2 == 0) && (k3 == 0))
+ {
+ this.representation = Tpb;
+ }
+ else
+ {
+ this.representation = Ppb;
+ }
+ }
+
+ public override BigInteger ToBigInteger()
+ {
+ return x.ToBigInteger();
+ }
+
+ public override string FieldName
+ {
+ get { return "F2m"; }
+ }
+
+ public override int FieldSize
+ {
+ get { return m; }
+ }
+
+ /**
+ * Checks, if the ECFieldElements a
and b
+ * are elements of the same field F2m
+ * (having the same representation).
+ * @param a field element.
+ * @param b field element to be compared.
+ * @throws ArgumentException if a
and b
+ * are not elements of the same field
+ * F2m
(having the same
+ * representation).
+ */
+ public static void CheckFieldElements(
+ ECFieldElement a,
+ ECFieldElement b)
+ {
+ if (!(a is F2mFieldElement) || !(b is F2mFieldElement))
+ {
+ throw new ArgumentException("Field elements are not "
+ + "both instances of F2mFieldElement");
+ }
+
+ F2mFieldElement aF2m = (F2mFieldElement)a;
+ F2mFieldElement bF2m = (F2mFieldElement)b;
+
+ if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1)
+ || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3))
+ {
+ throw new ArgumentException("Field elements are not "
+ + "elements of the same field F2m");
+ }
+
+ if (aF2m.representation != bF2m.representation)
+ {
+ // Should never occur
+ throw new ArgumentException(
+ "One of the field "
+ + "elements are not elements has incorrect representation");
+ }
+ }
+
+ public override ECFieldElement Add(
+ ECFieldElement b)
+ {
+ // No check performed here for performance reasons. Instead the
+ // elements involved are checked in ECPoint.F2m
+ // checkFieldElements(this, b);
+ IntArray iarrClone = (IntArray) this.x.Clone();
+ F2mFieldElement bF2m = (F2mFieldElement) b;
+ iarrClone.AddShifted(bF2m.x, 0);
+ return new F2mFieldElement(m, k1, k2, k3, iarrClone);
+ }
+
+ public override ECFieldElement Subtract(
+ ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return Add(b);
+ }
+
+ public override ECFieldElement Multiply(
+ ECFieldElement b)
+ {
+ // Right-to-left comb multiplication in the IntArray
+ // Input: Binary polynomials a(z) and b(z) of degree at most m-1
+ // Output: c(z) = a(z) * b(z) mod f(z)
+
+ // No check performed here for performance reasons. Instead the
+ // elements involved are checked in ECPoint.F2m
+ // checkFieldElements(this, b);
+ F2mFieldElement bF2m = (F2mFieldElement) b;
+ IntArray mult = x.Multiply(bF2m.x, m);
+ mult.Reduce(m, new int[]{k1, k2, k3});
+ return new F2mFieldElement(m, k1, k2, k3, mult);
+ }
+
+ public override ECFieldElement Divide(
+ ECFieldElement b)
+ {
+ // There may be more efficient implementations
+ ECFieldElement bInv = b.Invert();
+ return Multiply(bInv);
+ }
+
+ public override ECFieldElement Negate()
+ {
+ // -x == x holds for all x in F2m
+ return this;
+ }
+
+ public override ECFieldElement Square()
+ {
+ IntArray squared = x.Square(m);
+ squared.Reduce(m, new int[]{k1, k2, k3});
+ return new F2mFieldElement(m, k1, k2, k3, squared);
+ }
+
+ public override ECFieldElement Invert()
+ {
+ // Inversion in F2m using the extended Euclidean algorithm
+ // Input: A nonzero polynomial a(z) of degree at most m-1
+ // Output: a(z)^(-1) mod f(z)
+
+ // u(z) := a(z)
+ IntArray uz = (IntArray)this.x.Clone();
+
+ // v(z) := f(z)
+ IntArray vz = new IntArray(t);
+ vz.SetBit(m);
+ vz.SetBit(0);
+ vz.SetBit(this.k1);
+ if (this.representation == Ppb)
+ {
+ vz.SetBit(this.k2);
+ vz.SetBit(this.k3);
+ }
+
+ // g1(z) := 1, g2(z) := 0
+ IntArray g1z = new IntArray(t);
+ g1z.SetBit(0);
+ IntArray g2z = new IntArray(t);
+
+ // while u != 0
+ while (uz.GetUsedLength() > 0)
+// while (uz.bitLength() > 1)
+ {
+ // j := deg(u(z)) - deg(v(z))
+ int j = uz.BitLength - vz.BitLength;
+
+ // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j
+ if (j < 0)
+ {
+ IntArray uzCopy = uz;
+ uz = vz;
+ vz = uzCopy;
+
+ IntArray g1zCopy = g1z;
+ g1z = g2z;
+ g2z = g1zCopy;
+
+ j = -j;
+ }
+
+ // u(z) := u(z) + z^j * v(z)
+ // Note, that no reduction modulo f(z) is required, because
+ // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z)))
+ // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z))
+ // = deg(u(z))
+ // uz = uz.xor(vz.ShiftLeft(j));
+ // jInt = n / 32
+ int jInt = j >> 5;
+ // jInt = n % 32
+ int jBit = j & 0x1F;
+ IntArray vzShift = vz.ShiftLeft(jBit);
+ uz.AddShifted(vzShift, jInt);
+
+ // g1(z) := g1(z) + z^j * g2(z)
+// g1z = g1z.xor(g2z.ShiftLeft(j));
+ IntArray g2zShift = g2z.ShiftLeft(jBit);
+ g1z.AddShifted(g2zShift, jInt);
+ }
+ return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, g2z);
+ }
+
+ public override ECFieldElement Sqrt()
+ {
+ throw new ArithmeticException("Not implemented");
+ }
+
+ /**
+ * @return the representation of the field
+ * F2m
, either of
+ * {@link F2mFieldElement.Tpb} (trinomial
+ * basis representation) or
+ * {@link F2mFieldElement.Ppb} (pentanomial
+ * basis representation).
+ */
+ public int Representation
+ {
+ get { return this.representation; }
+ }
+
+ /**
+ * @return the degree m
of the reduction polynomial
+ * f(z)
.
+ */
+ public int M
+ {
+ get { return this.m; }
+ }
+
+ /**
+ * @return Tpb: The integer k
where xm +
+ * xk + 1
represents the reduction polynomial
+ * f(z)
.k1
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k2
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.0
k3
where xm +
+ * xk3 + xk2 + xk1 + 1
+ * represents the reduction polynomial f(z)
.ECMultiplier
.
+// * @param multiplier The ECMultiplier
to be used to multiply
+// * this ECPoint
.
+// */
+// internal void SetECMultiplier(
+// ECMultiplier multiplier)
+// {
+// this.multiplier = multiplier;
+// }
+
+ /**
+ * Sets the PreCompInfo
. Used by ECMultiplier
s
+ * to save the precomputation for this ECPoint
to store the
+ * precomputation result for use by subsequent multiplication.
+ * @param preCompInfo The values precomputed by the
+ * ECMultiplier
.
+ */
+ internal void SetPreCompInfo(
+ PreCompInfo preCompInfo)
+ {
+ this.preCompInfo = preCompInfo;
+ }
+
+ public abstract byte[] GetEncoded();
+
+ public abstract ECPoint Add(ECPoint b);
+ public abstract ECPoint Subtract(ECPoint b);
+ public abstract ECPoint Negate();
+ public abstract ECPoint Twice();
+ public abstract ECPoint Multiply(BigInteger b);
+
+ /**
+ * Sets the appropriate ECMultiplier
, unless already set.
+ */
+ internal virtual void AssertECMultiplier()
+ {
+ if (this.multiplier == null)
+ {
+ lock (this)
+ {
+ if (this.multiplier == null)
+ {
+ this.multiplier = new FpNafMultiplier();
+ }
+ }
+ }
+ }
+ }
+
+ public abstract class ECPointBase
+ : ECPoint
+ {
+ protected internal ECPointBase(
+ ECCurve curve,
+ ECFieldElement x,
+ ECFieldElement y,
+ bool withCompression)
+ : base(curve, x, y, withCompression)
+ {
+ }
+
+ protected internal abstract bool YTilde { get; }
+
+ /**
+ * return the field element encoded with point compression. (S 4.3.6)
+ */
+ public override byte[] GetEncoded()
+ {
+ if (this.IsInfinity)
+ return new byte[1];
+
+ // Note: some of the tests rely on calculating byte length from the field element
+ // (since the test cases use mismatching fields for curve/elements)
+ int byteLength = X9IntegerConverter.GetByteLength(x);
+ byte[] X = X9IntegerConverter.IntegerToBytes(this.X.ToBigInteger(), byteLength);
+ byte[] PO;
+
+ if (withCompression)
+ {
+ PO = new byte[1 + X.Length];
+
+ PO[0] = (byte)(YTilde ? 0x03 : 0x02);
+ }
+ else
+ {
+ byte[] Y = X9IntegerConverter.IntegerToBytes(this.Y.ToBigInteger(), byteLength);
+ PO = new byte[1 + X.Length + Y.Length];
+
+ PO[0] = 0x04;
+
+ Y.CopyTo(PO, 1 + X.Length);
+ }
+
+ X.CopyTo(PO, 1);
+
+ return PO;
+ }
+
+ /**
+ * Multiplies this ECPoint
by the given number.
+ * @param k The multiplicator.
+ * @return k * this
.
+ */
+ public override ECPoint Multiply(
+ BigInteger k)
+ {
+ if (this.IsInfinity)
+ return this;
+
+ if (k.SignValue == 0)
+ return this.curve.Infinity;
+
+ AssertECMultiplier();
+ return this.multiplier.Multiply(this, k, preCompInfo);
+ }
+ }
+
+ /**
+ * Elliptic curve points over Fp
+ */
+ public class FpPoint
+ : ECPointBase
+ {
+ /**
+ * Create a point which encodes with point compression.
+ *
+ * @param curve the curve to use
+ * @param x affine x co-ordinate
+ * @param y affine y co-ordinate
+ */
+ public FpPoint(
+ ECCurve curve,
+ ECFieldElement x,
+ ECFieldElement y)
+ : this(curve, x, y, false)
+ {
+ }
+
+ /**
+ * Create a point that encodes with or without point compresion.
+ *
+ * @param curve the curve to use
+ * @param x affine x co-ordinate
+ * @param y affine y co-ordinate
+ * @param withCompression if true encode with point compression
+ */
+ public FpPoint(
+ ECCurve curve,
+ ECFieldElement x,
+ ECFieldElement y,
+ bool withCompression)
+ : base(curve, x, y, withCompression)
+ {
+ if ((x != null && y == null) || (x == null && y != null))
+ throw new ArgumentException("Exactly one of the field elements is null");
+ }
+
+ protected internal override bool YTilde
+ {
+ get
+ {
+ return this.Y.ToBigInteger().TestBit(0);
+ }
+ }
+
+ // B.3 pg 62
+ public override ECPoint Add(
+ ECPoint b)
+ {
+ if (this.IsInfinity)
+ return b;
+
+ if (b.IsInfinity)
+ return this;
+
+ // Check if b = this or b = -this
+ if (this.x.Equals(b.x))
+ {
+ if (this.y.Equals(b.y))
+ {
+ // this = b, i.e. this must be doubled
+ return this.Twice();
+ }
+
+ Debug.Assert(this.y.Equals(b.y.Negate()));
+
+ // this = -b, i.e. the result is the point at infinity
+ return this.curve.Infinity;
+ }
+
+ ECFieldElement gamma = b.y.Subtract(this.y).Divide(b.x.Subtract(this.x));
+
+ ECFieldElement x3 = gamma.Square().Subtract(this.x).Subtract(b.x);
+ ECFieldElement y3 = gamma.Multiply(this.x.Subtract(x3)).Subtract(this.y);
+
+ return new FpPoint(curve, x3, y3);
+ }
+
+ // B.3 pg 62
+ public override ECPoint Twice()
+ {
+ // Twice identity element (point at infinity) is identity
+ if (this.IsInfinity)
+ return this;
+
+ // if y1 == 0, then (x1, y1) == (x1, -y1)
+ // and hence this = -this and thus 2(x1, y1) == infinity
+ if (this.y.ToBigInteger().SignValue == 0)
+ return this.curve.Infinity;
+
+ ECFieldElement TWO = this.curve.FromBigInteger(BigInteger.Two);
+ ECFieldElement THREE = this.curve.FromBigInteger(BigInteger.Three);
+ ECFieldElement gamma = this.x.Square().Multiply(THREE).Add(curve.a).Divide(y.Multiply(TWO));
+
+ ECFieldElement x3 = gamma.Square().Subtract(this.x.Multiply(TWO));
+ ECFieldElement y3 = gamma.Multiply(this.x.Subtract(x3)).Subtract(this.y);
+
+ return new FpPoint(curve, x3, y3, this.withCompression);
+ }
+
+ // D.3.2 pg 102 (see Note:)
+ public override ECPoint Subtract(
+ ECPoint b)
+ {
+ if (b.IsInfinity)
+ return this;
+
+ // Add -b
+ return Add(b.Negate());
+ }
+
+ public override ECPoint Negate()
+ {
+ return new FpPoint(this.curve, this.x, this.y.Negate(), this.withCompression);
+ }
+
+ // TODO Uncomment this to enable WNAF Fp point multiplication
+// /**
+// * Sets the default ECMultiplier
, unless already set.
+// */
+// internal override void AssertECMultiplier()
+// {
+// if (this.multiplier == null)
+// {
+// lock (this)
+// {
+// if (this.multiplier == null)
+// {
+// this.multiplier = new WNafMultiplier();
+// }
+// }
+// }
+// }
+ }
+
+ /**
+ * Elliptic curve points over F2m
+ */
+ public class F2mPoint
+ : ECPointBase
+ {
+ /**
+ * @param curve base curve
+ * @param x x point
+ * @param y y point
+ */
+ public F2mPoint(
+ ECCurve curve,
+ ECFieldElement x,
+ ECFieldElement y)
+ : this(curve, x, y, false)
+ {
+ }
+
+ /**
+ * @param curve base curve
+ * @param x x point
+ * @param y y point
+ * @param withCompression true if encode with point compression.
+ */
+ public F2mPoint(
+ ECCurve curve,
+ ECFieldElement x,
+ ECFieldElement y,
+ bool withCompression)
+ : base(curve, x, y, withCompression)
+ {
+ if ((x != null && y == null) || (x == null && y != null))
+ {
+ throw new ArgumentException("Exactly one of the field elements is null");
+ }
+
+ if (x != null)
+ {
+ // Check if x and y are elements of the same field
+ F2mFieldElement.CheckFieldElements(this.x, this.y);
+
+ // Check if x and a are elements of the same field
+ F2mFieldElement.CheckFieldElements(this.x, this.curve.A);
+ }
+ }
+
+ /**
+ * Constructor for point at infinity
+ */
+ [Obsolete("Use ECCurve.Infinity property")]
+ public F2mPoint(
+ ECCurve curve)
+ : this(curve, null, null)
+ {
+ }
+
+ protected internal override bool YTilde
+ {
+ get
+ {
+ // X9.62 4.2.2 and 4.3.6:
+ // if x = 0 then ypTilde := 0, else ypTilde is the rightmost
+ // bit of y * x^(-1)
+ return this.X.ToBigInteger().SignValue != 0
+ && this.Y.Multiply(this.X.Invert()).ToBigInteger().TestBit(0);
+ }
+ }
+
+ /**
+ * Check, if two ECPoint
s can be added or subtracted.
+ * @param a The first ECPoint
to check.
+ * @param b The second ECPoint
to check.
+ * @throws IllegalArgumentException if a
and b
+ * cannot be added.
+ */
+ private static void CheckPoints(
+ ECPoint a,
+ ECPoint b)
+ {
+ // Check, if points are on the same curve
+ if (!a.curve.Equals(b.curve))
+ throw new ArgumentException("Only points on the same curve can be added or subtracted");
+
+// F2mFieldElement.CheckFieldElements(a.x, b.x);
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.math.ec.ECPoint#add(org.bouncycastle.math.ec.ECPoint)
+ */
+ public override ECPoint Add(ECPoint b)
+ {
+ CheckPoints(this, b);
+ return AddSimple((F2mPoint) b);
+ }
+
+ /**
+ * Adds another ECPoints.F2m
to this
without
+ * checking if both points are on the same curve. Used by multiplication
+ * algorithms, because there all points are a multiple of the same point
+ * and hence the checks can be omitted.
+ * @param b The other ECPoints.F2m
to add to
+ * this
.
+ * @return this + b
+ */
+ internal F2mPoint AddSimple(F2mPoint b)
+ {
+ if (this.IsInfinity)
+ return b;
+
+ if (b.IsInfinity)
+ return this;
+
+ F2mFieldElement x2 = (F2mFieldElement) b.X;
+ F2mFieldElement y2 = (F2mFieldElement) b.Y;
+
+ // Check if b == this or b == -this
+ if (this.x.Equals(x2))
+ {
+ // this == b, i.e. this must be doubled
+ if (this.y.Equals(y2))
+ return (F2mPoint) this.Twice();
+
+ // this = -other, i.e. the result is the point at infinity
+ return (F2mPoint) this.curve.Infinity;
+ }
+
+ ECFieldElement xSum = this.x.Add(x2);
+
+ F2mFieldElement lambda
+ = (F2mFieldElement)(this.y.Add(y2)).Divide(xSum);
+
+ F2mFieldElement x3
+ = (F2mFieldElement)lambda.Square().Add(lambda).Add(xSum).Add(this.curve.A);
+
+ F2mFieldElement y3
+ = (F2mFieldElement)lambda.Multiply(this.x.Add(x3)).Add(x3).Add(this.y);
+
+ return new F2mPoint(curve, x3, y3, withCompression);
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.math.ec.ECPoint#subtract(org.bouncycastle.math.ec.ECPoint)
+ */
+ public override ECPoint Subtract(
+ ECPoint b)
+ {
+ CheckPoints(this, b);
+ return SubtractSimple((F2mPoint) b);
+ }
+
+ /**
+ * Subtracts another ECPoints.F2m
from this
+ * without checking if both points are on the same curve. Used by
+ * multiplication algorithms, because there all points are a multiple
+ * of the same point and hence the checks can be omitted.
+ * @param b The other ECPoints.F2m
to subtract from
+ * this
.
+ * @return this - b
+ */
+ internal F2mPoint SubtractSimple(
+ F2mPoint b)
+ {
+ if (b.IsInfinity)
+ return this;
+
+ // Add -b
+ return AddSimple((F2mPoint) b.Negate());
+ }
+
+ /* (non-Javadoc)
+ * @see Org.BouncyCastle.Math.EC.ECPoint#twice()
+ */
+ public override ECPoint Twice()
+ {
+ // Twice identity element (point at infinity) is identity
+ if (this.IsInfinity)
+ return this;
+
+ // if x1 == 0, then (x1, y1) == (x1, x1 + y1)
+ // and hence this = -this and thus 2(x1, y1) == infinity
+ if (this.x.ToBigInteger().SignValue == 0)
+ return this.curve.Infinity;
+
+ F2mFieldElement lambda = (F2mFieldElement) this.x.Add(this.y.Divide(this.x));
+ F2mFieldElement x2 = (F2mFieldElement)lambda.Square().Add(lambda).Add(this.curve.A);
+ ECFieldElement ONE = this.curve.FromBigInteger(BigInteger.One);
+ F2mFieldElement y2 = (F2mFieldElement)this.x.Square().Add(
+ x2.Multiply(lambda.Add(ONE)));
+
+ return new F2mPoint(this.curve, x2, y2, withCompression);
+ }
+
+ public override ECPoint Negate()
+ {
+ return new F2mPoint(curve, this.x, this.x.Add(this.y), withCompression);
+ }
+
+ // TODO Uncomment this to enable WNAF/WTNAF F2m point multiplication
+// /**
+// * Sets the appropriate ECMultiplier
, unless already set.
+// */
+// internal override void AssertECMultiplier()
+// {
+// if (this.multiplier == null)
+// {
+// lock (this)
+// {
+// if (this.multiplier == null)
+// {
+// if (((F2mCurve) this.curve).IsKoblitz)
+// {
+// this.multiplier = new WTauNafMultiplier();
+// }
+// else
+// {
+// this.multiplier = new WNafMultiplier();
+// }
+// }
+// }
+// }
+// }
+ }
+}
diff --git a/src/core/srcbc/math/ec/IntArray.cs b/src/core/srcbc/math/ec/IntArray.cs
new file mode 100644
index 0000000..d16197d
--- /dev/null
+++ b/src/core/srcbc/math/ec/IntArray.cs
@@ -0,0 +1,486 @@
+using System;
+using System.Text;
+
+namespace Org.BouncyCastle.Math.EC
+{
+ internal class IntArray
+ : ICloneable
+ {
+ // TODO make m fixed for the IntArray, and hence compute T once and for all
+
+ // TODO Use uint's internally?
+ private int[] m_ints;
+
+ public IntArray(int intLen)
+ {
+ m_ints = new int[intLen];
+ }
+
+ private IntArray(int[] ints)
+ {
+ m_ints = ints;
+ }
+
+ public IntArray(BigInteger bigInt)
+ : this(bigInt, 0)
+ {
+ }
+
+ public IntArray(BigInteger bigInt, int minIntLen)
+ {
+ if (bigInt.SignValue == -1)
+ throw new ArgumentException("Only positive Integers allowed", "bigint");
+
+ if (bigInt.SignValue == 0)
+ {
+ m_ints = new int[] { 0 };
+ return;
+ }
+
+ byte[] barr = bigInt.ToByteArrayUnsigned();
+ int barrLen = barr.Length;
+
+ int intLen = (barrLen + 3) / 4;
+ m_ints = new int[System.Math.Max(intLen, minIntLen)];
+
+ int rem = barrLen % 4;
+ int barrI = 0;
+
+ if (0 < rem)
+ {
+ int temp = (int) barr[barrI++];
+ while (barrI < rem)
+ {
+ temp = temp << 8 | (int) barr[barrI++];
+ }
+ m_ints[--intLen] = temp;
+ }
+
+ while (intLen > 0)
+ {
+ int temp = (int) barr[barrI++];
+ for (int i = 1; i < 4; i++)
+ {
+ temp = temp << 8 | (int) barr[barrI++];
+ }
+ m_ints[--intLen] = temp;
+ }
+ }
+
+ public int GetUsedLength()
+ {
+ int highestIntPos = m_ints.Length;
+
+ if (highestIntPos < 1)
+ return 0;
+
+ // Check if first element will act as sentinel
+ if (m_ints[0] != 0)
+ {
+ while (m_ints[--highestIntPos] == 0)
+ {
+ }
+ return highestIntPos + 1;
+ }
+
+ do
+ {
+ if (m_ints[--highestIntPos] != 0)
+ {
+ return highestIntPos + 1;
+ }
+ }
+ while (highestIntPos > 0);
+
+ return 0;
+ }
+
+ public int BitLength
+ {
+ get
+ {
+ // JDK 1.5: see Integer.numberOfLeadingZeros()
+ int intLen = GetUsedLength();
+ if (intLen == 0)
+ return 0;
+
+ int last = intLen - 1;
+ uint highest = (uint) m_ints[last];
+ int bits = (last << 5) + 1;
+
+ // A couple of binary search steps
+ if (highest > 0x0000ffff)
+ {
+ if (highest > 0x00ffffff)
+ {
+ bits += 24;
+ highest >>= 24;
+ }
+ else
+ {
+ bits += 16;
+ highest >>= 16;
+ }
+ }
+ else if (highest > 0x000000ff)
+ {
+ bits += 8;
+ highest >>= 8;
+ }
+
+ while (highest > 1)
+ {
+ ++bits;
+ highest >>= 1;
+ }
+
+ return bits;
+ }
+ }
+
+ private int[] resizedInts(int newLen)
+ {
+ int[] newInts = new int[newLen];
+ int oldLen = m_ints.Length;
+ int copyLen = oldLen < newLen ? oldLen : newLen;
+ Array.Copy(m_ints, 0, newInts, 0, copyLen);
+ return newInts;
+ }
+
+ public BigInteger ToBigInteger()
+ {
+ int usedLen = GetUsedLength();
+ if (usedLen == 0)
+ {
+ return BigInteger.Zero;
+ }
+
+ int highestInt = m_ints[usedLen - 1];
+ byte[] temp = new byte[4];
+ int barrI = 0;
+ bool trailingZeroBytesDone = false;
+ for (int j = 3; j >= 0; j--)
+ {
+ byte thisByte = (byte)((int)((uint) highestInt >> (8 * j)));
+ if (trailingZeroBytesDone || (thisByte != 0))
+ {
+ trailingZeroBytesDone = true;
+ temp[barrI++] = thisByte;
+ }
+ }
+
+ int barrLen = 4 * (usedLen - 1) + barrI;
+ byte[] barr = new byte[barrLen];
+ for (int j = 0; j < barrI; j++)
+ {
+ barr[j] = temp[j];
+ }
+ // Highest value int is done now
+
+ for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--)
+ {
+ for (int j = 3; j >= 0; j--)
+ {
+ barr[barrI++] = (byte)((int)((uint)m_ints[iarrJ] >> (8 * j)));
+ }
+ }
+ return new BigInteger(1, barr);
+ }
+
+ public void ShiftLeft()
+ {
+ int usedLen = GetUsedLength();
+ if (usedLen == 0)
+ {
+ return;
+ }
+ if (m_ints[usedLen - 1] < 0)
+ {
+ // highest bit of highest used byte is set, so shifting left will
+ // make the IntArray one byte longer
+ usedLen++;
+ if (usedLen > m_ints.Length)
+ {
+ // make the m_ints one byte longer, because we need one more
+ // byte which is not available in m_ints
+ m_ints = resizedInts(m_ints.Length + 1);
+ }
+ }
+
+ bool carry = false;
+ for (int i = 0; i < usedLen; i++)
+ {
+ // nextCarry is true if highest bit is set
+ bool nextCarry = m_ints[i] < 0;
+ m_ints[i] <<= 1;
+ if (carry)
+ {
+ // set lowest bit
+ m_ints[i] |= 1;
+ }
+ carry = nextCarry;
+ }
+ }
+
+ public IntArray ShiftLeft(int n)
+ {
+ int usedLen = GetUsedLength();
+ if (usedLen == 0)
+ {
+ return this;
+ }
+
+ if (n == 0)
+ {
+ return this;
+ }
+
+ if (n > 31)
+ {
+ throw new ArgumentException("shiftLeft() for max 31 bits "
+ + ", " + n + "bit shift is not possible", "n");
+ }
+
+ int[] newInts = new int[usedLen + 1];
+
+ int nm32 = 32 - n;
+ newInts[0] = m_ints[0] << n;
+ for (int i = 1; i < usedLen; i++)
+ {
+ newInts[i] = (m_ints[i] << n) | (int)((uint)m_ints[i - 1] >> nm32);
+ }
+ newInts[usedLen] = (int)((uint)m_ints[usedLen - 1] >> nm32);
+
+ return new IntArray(newInts);
+ }
+
+ public void AddShifted(IntArray other, int shift)
+ {
+ int usedLenOther = other.GetUsedLength();
+ int newMinUsedLen = usedLenOther + shift;
+ if (newMinUsedLen > m_ints.Length)
+ {
+ m_ints = resizedInts(newMinUsedLen);
+ //Console.WriteLine("Resize required");
+ }
+
+ for (int i = 0; i < usedLenOther; i++)
+ {
+ m_ints[i + shift] ^= other.m_ints[i];
+ }
+ }
+
+ public int Length
+ {
+ get { return m_ints.Length; }
+ }
+
+ public bool TestBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int tester = 1 << theBit;
+ return ((m_ints[theInt] & tester) != 0);
+ }
+
+ public void FlipBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int flipper = 1 << theBit;
+ m_ints[theInt] ^= flipper;
+ }
+
+ public void SetBit(int n)
+ {
+ // theInt = n / 32
+ int theInt = n >> 5;
+ // theBit = n % 32
+ int theBit = n & 0x1F;
+ int setter = 1 << theBit;
+ m_ints[theInt] |= setter;
+ }
+
+ public IntArray Multiply(IntArray other, int m)
+ {
+ // Lenght of c is 2m bits rounded up to the next int (32 bit)
+ int t = (m + 31) >> 5;
+ if (m_ints.Length < t)
+ {
+ m_ints = resizedInts(t);
+ }
+
+ IntArray b = new IntArray(other.resizedInts(other.Length + 1));
+ IntArray c = new IntArray((m + m + 31) >> 5);
+ // IntArray c = new IntArray(t + t);
+ int testBit = 1;
+ for (int k = 0; k < 32; k++)
+ {
+ for (int j = 0; j < t; j++)
+ {
+ if ((m_ints[j] & testBit) != 0)
+ {
+ // The kth bit of m_ints[j] is set
+ c.AddShifted(b, j);
+ }
+ }
+ testBit <<= 1;
+ b.ShiftLeft();
+ }
+ return c;
+ }
+
+ // public IntArray multiplyLeftToRight(IntArray other, int m) {
+ // // Lenght of c is 2m bits rounded up to the next int (32 bit)
+ // int t = (m + 31) / 32;
+ // if (m_ints.Length < t) {
+ // m_ints = resizedInts(t);
+ // }
+ //
+ // IntArray b = new IntArray(other.resizedInts(other.getLength() + 1));
+ // IntArray c = new IntArray((m + m + 31) / 32);
+ // // IntArray c = new IntArray(t + t);
+ // int testBit = 1 << 31;
+ // for (int k = 31; k >= 0; k--) {
+ // for (int j = 0; j < t; j++) {
+ // if ((m_ints[j] & testBit) != 0) {
+ // // The kth bit of m_ints[j] is set
+ // c.addShifted(b, j);
+ // }
+ // }
+ // testBit >>>= 1;
+ // if (k > 0) {
+ // c.shiftLeft();
+ // }
+ // }
+ // return c;
+ // }
+
+ // TODO note, redPol.Length must be 3 for TPB and 5 for PPB
+ public void Reduce(int m, int[] redPol)
+ {
+ for (int i = m + m - 2; i >= m; i--)
+ {
+ if (TestBit(i))
+ {
+ int bit = i - m;
+ FlipBit(bit);
+ FlipBit(i);
+ int l = redPol.Length;
+ while (--l >= 0)
+ {
+ FlipBit(redPol[l] + bit);
+ }
+ }
+ }
+ m_ints = resizedInts((m + 31) >> 5);
+ }
+
+ public IntArray Square(int m)
+ {
+ // TODO make the table static readonly
+ int[] table = { 0x0, 0x1, 0x4, 0x5, 0x10, 0x11, 0x14, 0x15, 0x40,
+ 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55 };
+
+ int t = (m + 31) >> 5;
+ if (m_ints.Length < t)
+ {
+ m_ints = resizedInts(t);
+ }
+
+ IntArray c = new IntArray(t + t);
+
+ // TODO twice the same code, put in separate private method
+ for (int i = 0; i < t; i++)
+ {
+ int v0 = 0;
+ for (int j = 0; j < 4; j++)
+ {
+ v0 = (int)((uint) v0 >> 8);
+ int u = (int)((uint)m_ints[i] >> (j * 4)) & 0xF;
+ int w = table[u] << 24;
+ v0 |= w;
+ }
+ c.m_ints[i + i] = v0;
+
+ v0 = 0;
+ int upper = (int)((uint) m_ints[i] >> 16);
+ for (int j = 0; j < 4; j++)
+ {
+ v0 = (int)((uint) v0 >> 8);
+ int u = (int)((uint)upper >> (j * 4)) & 0xF;
+ int w = table[u] << 24;
+ v0 |= w;
+ }
+ c.m_ints[i + i + 1] = v0;
+ }
+ return c;
+ }
+
+ public override bool Equals(object o)
+ {
+ if (!(o is IntArray))
+ {
+ return false;
+ }
+ IntArray other = (IntArray) o;
+ int usedLen = GetUsedLength();
+ if (other.GetUsedLength() != usedLen)
+ {
+ return false;
+ }
+ for (int i = 0; i < usedLen; i++)
+ {
+ if (m_ints[i] != other.m_ints[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ int i = GetUsedLength();
+ int hc = i;
+ while (--i >= 0)
+ {
+ hc *= 17;
+ hc ^= m_ints[i];
+ }
+ return hc;
+ }
+
+ public object Clone()
+ {
+ return new IntArray((int[]) m_ints.Clone());
+ }
+
+ public override string ToString()
+ {
+ int usedLen = GetUsedLength();
+ if (usedLen == 0)
+ {
+ return "0";
+ }
+
+ StringBuilder sb = new StringBuilder(Convert.ToString(m_ints[usedLen - 1], 2));
+ for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--)
+ {
+ string hexString = Convert.ToString(m_ints[iarrJ], 2);
+
+ // Add leading zeroes, except for highest significant int
+ for (int i = hexString.Length; i < 8; i++)
+ {
+ hexString = "0" + hexString;
+ }
+ sb.Append(hexString);
+ }
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/core/srcbc/math/ec/abc/SimpleBigDecimal.cs b/src/core/srcbc/math/ec/abc/SimpleBigDecimal.cs
new file mode 100644
index 0000000..47c165e
--- /dev/null
+++ b/src/core/srcbc/math/ec/abc/SimpleBigDecimal.cs
@@ -0,0 +1,241 @@
+using System;
+using System.Text;
+
+namespace Org.BouncyCastle.Math.EC.Abc
+{
+ /**
+ * Class representing a simple version of a big decimal. A
+ * SimpleBigDecimal
is basically a
+ * {@link java.math.BigInteger BigInteger} with a few digits on the right of
+ * the decimal point. The number of (binary) digits on the right of the decimal
+ * point is called the scale
of the SimpleBigDecimal
.
+ * Unlike in {@link java.math.BigDecimal BigDecimal}, the scale is not adjusted
+ * automatically, but must be set manually. All SimpleBigDecimal
s
+ * taking part in the same arithmetic operation must have equal scale. The
+ * result of a multiplication of two SimpleBigDecimal
s returns a
+ * SimpleBigDecimal
with double scale.
+ */
+ internal class SimpleBigDecimal
+ // : Number
+ {
+ // private static final long serialVersionUID = 1L;
+
+ private readonly BigInteger bigInt;
+ private readonly int scale;
+
+ /**
+ * Returns a SimpleBigDecimal
representing the same numerical
+ * value as value
.
+ * @param value The value of the SimpleBigDecimal
to be
+ * created.
+ * @param scale The scale of the SimpleBigDecimal
to be
+ * created.
+ * @return The such created SimpleBigDecimal
.
+ */
+ public static SimpleBigDecimal GetInstance(BigInteger val, int scale)
+ {
+ return new SimpleBigDecimal(val.ShiftLeft(scale), scale);
+ }
+
+ /**
+ * Constructor for SimpleBigDecimal
. The value of the
+ * constructed SimpleBigDecimal
Equals bigInt /
+ * 2scale
.
+ * @param bigInt The bigInt
value parameter.
+ * @param scale The scale of the constructed SimpleBigDecimal
.
+ */
+ public SimpleBigDecimal(BigInteger bigInt, int scale)
+ {
+ if (scale < 0)
+ throw new ArgumentException("scale may not be negative");
+
+ this.bigInt = bigInt;
+ this.scale = scale;
+ }
+
+ private SimpleBigDecimal(SimpleBigDecimal limBigDec)
+ {
+ bigInt = limBigDec.bigInt;
+ scale = limBigDec.scale;
+ }
+
+ private void CheckScale(SimpleBigDecimal b)
+ {
+ if (scale != b.scale)
+ throw new ArgumentException("Only SimpleBigDecimal of same scale allowed in arithmetic operations");
+ }
+
+ public SimpleBigDecimal AdjustScale(int newScale)
+ {
+ if (newScale < 0)
+ throw new ArgumentException("scale may not be negative");
+
+ if (newScale == scale)
+ return this;
+
+ return new SimpleBigDecimal(bigInt.ShiftLeft(newScale - scale), newScale);
+ }
+
+ public SimpleBigDecimal Add(SimpleBigDecimal b)
+ {
+ CheckScale(b);
+ return new SimpleBigDecimal(bigInt.Add(b.bigInt), scale);
+ }
+
+ public SimpleBigDecimal Add(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.Add(b.ShiftLeft(scale)), scale);
+ }
+
+ public SimpleBigDecimal Negate()
+ {
+ return new SimpleBigDecimal(bigInt.Negate(), scale);
+ }
+
+ public SimpleBigDecimal Subtract(SimpleBigDecimal b)
+ {
+ return Add(b.Negate());
+ }
+
+ public SimpleBigDecimal Subtract(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.Subtract(b.ShiftLeft(scale)), scale);
+ }
+
+ public SimpleBigDecimal Multiply(SimpleBigDecimal b)
+ {
+ CheckScale(b);
+ return new SimpleBigDecimal(bigInt.Multiply(b.bigInt), scale + scale);
+ }
+
+ public SimpleBigDecimal Multiply(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.Multiply(b), scale);
+ }
+
+ public SimpleBigDecimal Divide(SimpleBigDecimal b)
+ {
+ CheckScale(b);
+ BigInteger dividend = bigInt.ShiftLeft(scale);
+ return new SimpleBigDecimal(dividend.Divide(b.bigInt), scale);
+ }
+
+ public SimpleBigDecimal Divide(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.Divide(b), scale);
+ }
+
+ public SimpleBigDecimal ShiftLeft(int n)
+ {
+ return new SimpleBigDecimal(bigInt.ShiftLeft(n), scale);
+ }
+
+ public int CompareTo(SimpleBigDecimal val)
+ {
+ CheckScale(val);
+ return bigInt.CompareTo(val.bigInt);
+ }
+
+ public int CompareTo(BigInteger val)
+ {
+ return bigInt.CompareTo(val.ShiftLeft(scale));
+ }
+
+ public BigInteger Floor()
+ {
+ return bigInt.ShiftRight(scale);
+ }
+
+ public BigInteger Round()
+ {
+ SimpleBigDecimal oneHalf = new SimpleBigDecimal(BigInteger.One, 1);
+ return Add(oneHalf.AdjustScale(scale)).Floor();
+ }
+
+ public int IntValue
+ {
+ get { return Floor().IntValue; }
+ }
+
+ public long LongValue
+ {
+ get { return Floor().LongValue; }
+ }
+
+// public double doubleValue()
+// {
+// return new Double(ToString()).doubleValue();
+// }
+//
+// public float floatValue()
+// {
+// return new Float(ToString()).floatValue();
+// }
+
+ public int Scale
+ {
+ get { return scale; }
+ }
+
+ public override string ToString()
+ {
+ if (scale == 0)
+ return bigInt.ToString();
+
+ BigInteger floorBigInt = Floor();
+
+ BigInteger fract = bigInt.Subtract(floorBigInt.ShiftLeft(scale));
+ if (bigInt.SignValue < 0)
+ {
+ fract = BigInteger.One.ShiftLeft(scale).Subtract(fract);
+ }
+
+ if ((floorBigInt.SignValue == -1) && (!(fract.Equals(BigInteger.Zero))))
+ {
+ floorBigInt = floorBigInt.Add(BigInteger.One);
+ }
+ string leftOfPoint = floorBigInt.ToString();
+
+ char[] fractCharArr = new char[scale];
+ string fractStr = fract.ToString(2);
+ int fractLen = fractStr.Length;
+ int zeroes = scale - fractLen;
+ for (int i = 0; i < zeroes; i++)
+ {
+ fractCharArr[i] = '0';
+ }
+ for (int j = 0; j < fractLen; j++)
+ {
+ fractCharArr[zeroes + j] = fractStr[j];
+ }
+ string rightOfPoint = new string(fractCharArr);
+
+ StringBuilder sb = new StringBuilder(leftOfPoint);
+ sb.Append(".");
+ sb.Append(rightOfPoint);
+
+ return sb.ToString();
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (this == obj)
+ return true;
+
+ SimpleBigDecimal other = obj as SimpleBigDecimal;
+
+ if (other == null)
+ return false;
+
+ return bigInt.Equals(other.bigInt)
+ && scale == other.scale;
+ }
+
+ public override int GetHashCode()
+ {
+ return bigInt.GetHashCode() ^ scale;
+ }
+
+ }
+}
diff --git a/src/core/srcbc/math/ec/abc/Tnaf.cs b/src/core/srcbc/math/ec/abc/Tnaf.cs
new file mode 100644
index 0000000..b73bcd1
--- /dev/null
+++ b/src/core/srcbc/math/ec/abc/Tnaf.cs
@@ -0,0 +1,834 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Abc
+{
+ /**
+ * Class holding methods for point multiplication based on the window
+ * τ-adic nonadjacent form (WTNAF). The algorithms are based on the
+ * paper "Improved Algorithms for Arithmetic on Anomalous Binary Curves"
+ * by Jerome A. Solinas. The paper first appeared in the Proceedings of
+ * Crypto 1997.
+ */
+ internal class Tnaf
+ {
+ private static readonly BigInteger MinusOne = BigInteger.One.Negate();
+ private static readonly BigInteger MinusTwo = BigInteger.Two.Negate();
+ private static readonly BigInteger MinusThree = BigInteger.Three.Negate();
+ private static readonly BigInteger Four = BigInteger.ValueOf(4);
+
+ /**
+ * The window width of WTNAF. The standard value of 4 is slightly less
+ * than optimal for running time, but keeps space requirements for
+ * precomputation low. For typical curves, a value of 5 or 6 results in
+ * a better running time. When changing this value, the
+ * αu
's must be computed differently, see
+ * e.g. "Guide to Elliptic Curve Cryptography", Darrel Hankerson,
+ * Alfred Menezes, Scott Vanstone, Springer-Verlag New York Inc., 2004,
+ * p. 121-122
+ */
+ public const sbyte Width = 4;
+
+ /**
+ * 24
+ */
+ public const sbyte Pow2Width = 16;
+
+ /**
+ * The αu
's for a=0
as an array
+ * of ZTauElement
s.
+ */
+ public static readonly ZTauElement[] Alpha0 =
+ {
+ null,
+ new ZTauElement(BigInteger.One, BigInteger.Zero), null,
+ new ZTauElement(MinusThree, MinusOne), null,
+ new ZTauElement(MinusOne, MinusOne), null,
+ new ZTauElement(BigInteger.One, MinusOne), null
+ };
+
+ /**
+ * The αu
's for a=0
as an array
+ * of TNAFs.
+ */
+ public static readonly sbyte[][] Alpha0Tnaf =
+ {
+ null, new sbyte[]{1}, null, new sbyte[]{-1, 0, 1}, null, new sbyte[]{1, 0, 1}, null, new sbyte[]{-1, 0, 0, 1}
+ };
+
+ /**
+ * The αu
's for a=1
as an array
+ * of ZTauElement
s.
+ */
+ public static readonly ZTauElement[] Alpha1 =
+ {
+ null,
+ new ZTauElement(BigInteger.One, BigInteger.Zero), null,
+ new ZTauElement(MinusThree, BigInteger.One), null,
+ new ZTauElement(MinusOne, BigInteger.One), null,
+ new ZTauElement(BigInteger.One, BigInteger.One), null
+ };
+
+ /**
+ * The αu
's for a=1
as an array
+ * of TNAFs.
+ */
+ public static readonly sbyte[][] Alpha1Tnaf =
+ {
+ null, new sbyte[]{1}, null, new sbyte[]{-1, 0, 1}, null, new sbyte[]{1, 0, 1}, null, new sbyte[]{-1, 0, 0, -1}
+ };
+
+ /**
+ * Computes the norm of an element λ
of
+ * Z[τ]
.
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param lambda The element λ
of
+ * Z[τ]
.
+ * @return The norm of λ
.
+ */
+ public static BigInteger Norm(sbyte mu, ZTauElement lambda)
+ {
+ BigInteger norm;
+
+ // s1 = u^2
+ BigInteger s1 = lambda.u.Multiply(lambda.u);
+
+ // s2 = u * v
+ BigInteger s2 = lambda.u.Multiply(lambda.v);
+
+ // s3 = 2 * v^2
+ BigInteger s3 = lambda.v.Multiply(lambda.v).ShiftLeft(1);
+
+ if (mu == 1)
+ {
+ norm = s1.Add(s2).Add(s3);
+ }
+ else if (mu == -1)
+ {
+ norm = s1.Subtract(s2).Add(s3);
+ }
+ else
+ {
+ throw new ArgumentException("mu must be 1 or -1");
+ }
+
+ return norm;
+ }
+
+ /**
+ * Computes the norm of an element λ
of
+ * R[τ]
, where λ = u + vτ
+ * and u
and u
are real numbers (elements of
+ * R
).
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param u The real part of the element λ
of
+ * R[τ]
.
+ * @param v The τ
-adic part of the element
+ * λ
of R[τ]
.
+ * @return The norm of λ
.
+ */
+ public static SimpleBigDecimal Norm(sbyte mu, SimpleBigDecimal u, SimpleBigDecimal v)
+ {
+ SimpleBigDecimal norm;
+
+ // s1 = u^2
+ SimpleBigDecimal s1 = u.Multiply(u);
+
+ // s2 = u * v
+ SimpleBigDecimal s2 = u.Multiply(v);
+
+ // s3 = 2 * v^2
+ SimpleBigDecimal s3 = v.Multiply(v).ShiftLeft(1);
+
+ if (mu == 1)
+ {
+ norm = s1.Add(s2).Add(s3);
+ }
+ else if (mu == -1)
+ {
+ norm = s1.Subtract(s2).Add(s3);
+ }
+ else
+ {
+ throw new ArgumentException("mu must be 1 or -1");
+ }
+
+ return norm;
+ }
+
+ /**
+ * Rounds an element λ
of R[τ]
+ * to an element of Z[τ]
, such that their difference
+ * has minimal norm. λ
is given as
+ * λ = λ0 + λ1τ
.
+ * @param lambda0 The component λ0
.
+ * @param lambda1 The component λ1
.
+ * @param mu The parameter μ
of the elliptic curve. Must
+ * equal 1 or -1.
+ * @return The rounded element of Z[τ]
.
+ * @throws ArgumentException if lambda0
and
+ * lambda1
do not have same scale.
+ */
+ public static ZTauElement Round(SimpleBigDecimal lambda0,
+ SimpleBigDecimal lambda1, sbyte mu)
+ {
+ int scale = lambda0.Scale;
+ if (lambda1.Scale != scale)
+ throw new ArgumentException("lambda0 and lambda1 do not have same scale");
+
+ if (!((mu == 1) || (mu == -1)))
+ throw new ArgumentException("mu must be 1 or -1");
+
+ BigInteger f0 = lambda0.Round();
+ BigInteger f1 = lambda1.Round();
+
+ SimpleBigDecimal eta0 = lambda0.Subtract(f0);
+ SimpleBigDecimal eta1 = lambda1.Subtract(f1);
+
+ // eta = 2*eta0 + mu*eta1
+ SimpleBigDecimal eta = eta0.Add(eta0);
+ if (mu == 1)
+ {
+ eta = eta.Add(eta1);
+ }
+ else
+ {
+ // mu == -1
+ eta = eta.Subtract(eta1);
+ }
+
+ // check1 = eta0 - 3*mu*eta1
+ // check2 = eta0 + 4*mu*eta1
+ SimpleBigDecimal threeEta1 = eta1.Add(eta1).Add(eta1);
+ SimpleBigDecimal fourEta1 = threeEta1.Add(eta1);
+ SimpleBigDecimal check1;
+ SimpleBigDecimal check2;
+ if (mu == 1)
+ {
+ check1 = eta0.Subtract(threeEta1);
+ check2 = eta0.Add(fourEta1);
+ }
+ else
+ {
+ // mu == -1
+ check1 = eta0.Add(threeEta1);
+ check2 = eta0.Subtract(fourEta1);
+ }
+
+ sbyte h0 = 0;
+ sbyte h1 = 0;
+
+ // if eta >= 1
+ if (eta.CompareTo(BigInteger.One) >= 0)
+ {
+ if (check1.CompareTo(MinusOne) < 0)
+ {
+ h1 = mu;
+ }
+ else
+ {
+ h0 = 1;
+ }
+ }
+ else
+ {
+ // eta < 1
+ if (check2.CompareTo(BigInteger.Two) >= 0)
+ {
+ h1 = mu;
+ }
+ }
+
+ // if eta < -1
+ if (eta.CompareTo(MinusOne) < 0)
+ {
+ if (check1.CompareTo(BigInteger.One) >= 0)
+ {
+ h1 = (sbyte)-mu;
+ }
+ else
+ {
+ h0 = -1;
+ }
+ }
+ else
+ {
+ // eta >= -1
+ if (check2.CompareTo(MinusTwo) < 0)
+ {
+ h1 = (sbyte)-mu;
+ }
+ }
+
+ BigInteger q0 = f0.Add(BigInteger.ValueOf(h0));
+ BigInteger q1 = f1.Add(BigInteger.ValueOf(h1));
+ return new ZTauElement(q0, q1);
+ }
+
+ /**
+ * Approximate division by n
. For an integer
+ * k
, the value λ = s k / n
is
+ * computed to c
bits of accuracy.
+ * @param k The parameter k
.
+ * @param s The curve parameter s0
or
+ * s1
.
+ * @param vm The Lucas Sequence element Vm
.
+ * @param a The parameter a
of the elliptic curve.
+ * @param m The bit length of the finite field
+ * Fm
.
+ * @param c The number of bits of accuracy, i.e. the scale of the returned
+ * SimpleBigDecimal
.
+ * @return The value λ = s k / n
computed to
+ * c
bits of accuracy.
+ */
+ public static SimpleBigDecimal ApproximateDivisionByN(BigInteger k,
+ BigInteger s, BigInteger vm, sbyte a, int m, int c)
+ {
+ int _k = (m + 5)/2 + c;
+ BigInteger ns = k.ShiftRight(m - _k - 2 + a);
+
+ BigInteger gs = s.Multiply(ns);
+
+ BigInteger hs = gs.ShiftRight(m);
+
+ BigInteger js = vm.Multiply(hs);
+
+ BigInteger gsPlusJs = gs.Add(js);
+ BigInteger ls = gsPlusJs.ShiftRight(_k-c);
+ if (gsPlusJs.TestBit(_k-c-1))
+ {
+ // round up
+ ls = ls.Add(BigInteger.One);
+ }
+
+ return new SimpleBigDecimal(ls, c);
+ }
+
+ /**
+ * Computes the τ
-adic NAF (non-adjacent form) of an
+ * element λ
of Z[τ]
.
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param lambda The element λ
of
+ * Z[τ]
.
+ * @return The τ
-adic NAF of λ
.
+ */
+ public static sbyte[] TauAdicNaf(sbyte mu, ZTauElement lambda)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ throw new ArgumentException("mu must be 1 or -1");
+
+ BigInteger norm = Norm(mu, lambda);
+
+ // Ceiling of log2 of the norm
+ int log2Norm = norm.BitLength;
+
+ // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+ int maxLength = log2Norm > 30 ? log2Norm + 4 : 34;
+
+ // The array holding the TNAF
+ sbyte[] u = new sbyte[maxLength];
+ int i = 0;
+
+ // The actual length of the TNAF
+ int length = 0;
+
+ BigInteger r0 = lambda.u;
+ BigInteger r1 = lambda.v;
+
+ while(!((r0.Equals(BigInteger.Zero)) && (r1.Equals(BigInteger.Zero))))
+ {
+ // If r0 is odd
+ if (r0.TestBit(0))
+ {
+ u[i] = (sbyte) BigInteger.Two.Subtract((r0.Subtract(r1.ShiftLeft(1))).Mod(Four)).IntValue;
+
+ // r0 = r0 - u[i]
+ if (u[i] == 1)
+ {
+ r0 = r0.ClearBit(0);
+ }
+ else
+ {
+ // u[i] == -1
+ r0 = r0.Add(BigInteger.One);
+ }
+ length = i;
+ }
+ else
+ {
+ u[i] = 0;
+ }
+
+ BigInteger t = r0;
+ BigInteger s = r0.ShiftRight(1);
+ if (mu == 1)
+ {
+ r0 = r1.Add(s);
+ }
+ else
+ {
+ // mu == -1
+ r0 = r1.Subtract(s);
+ }
+
+ r1 = t.ShiftRight(1).Negate();
+ i++;
+ }
+
+ length++;
+
+ // Reduce the TNAF array to its actual length
+ sbyte[] tnaf = new sbyte[length];
+ Array.Copy(u, 0, tnaf, 0, length);
+ return tnaf;
+ }
+
+ /**
+ * Applies the operation τ()
to an
+ * F2mPoint
.
+ * @param p The F2mPoint to which τ()
is applied.
+ * @return τ(p)
+ */
+ public static F2mPoint Tau(F2mPoint p)
+ {
+ if (p.IsInfinity)
+ return p;
+
+ ECFieldElement x = p.X;
+ ECFieldElement y = p.Y;
+
+ return new F2mPoint(p.Curve, x.Square(), y.Square(), p.IsCompressed);
+ }
+
+ /**
+ * Returns the parameter μ
of the elliptic curve.
+ * @param curve The elliptic curve from which to obtain μ
.
+ * The curve must be a Koblitz curve, i.e. a
Equals
+ * 0
or 1
and b
Equals
+ * 1
.
+ * @return μ
of the elliptic curve.
+ * @throws ArgumentException if the given ECCurve is not a Koblitz
+ * curve.
+ */
+ public static sbyte GetMu(F2mCurve curve)
+ {
+ BigInteger a = curve.A.ToBigInteger();
+
+ sbyte mu;
+ if (a.SignValue == 0)
+ {
+ mu = -1;
+ }
+ else if (a.Equals(BigInteger.One))
+ {
+ mu = 1;
+ }
+ else
+ {
+ throw new ArgumentException("No Koblitz curve (ABC), TNAF multiplication not possible");
+ }
+ return mu;
+ }
+
+ /**
+ * Calculates the Lucas Sequence elements Uk-1
and
+ * Uk
or Vk-1
and
+ * Vk
.
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param k The index of the second element of the Lucas Sequence to be
+ * returned.
+ * @param doV If set to true, computes Vk-1
and
+ * Vk
, otherwise Uk-1
and
+ * Uk
.
+ * @return An array with 2 elements, containing Uk-1
+ * and Uk
or Vk-1
+ * and Vk
.
+ */
+ public static BigInteger[] GetLucas(sbyte mu, int k, bool doV)
+ {
+ if (!(mu == 1 || mu == -1))
+ throw new ArgumentException("mu must be 1 or -1");
+
+ BigInteger u0;
+ BigInteger u1;
+ BigInteger u2;
+
+ if (doV)
+ {
+ u0 = BigInteger.Two;
+ u1 = BigInteger.ValueOf(mu);
+ }
+ else
+ {
+ u0 = BigInteger.Zero;
+ u1 = BigInteger.One;
+ }
+
+ for (int i = 1; i < k; i++)
+ {
+ // u2 = mu*u1 - 2*u0;
+ BigInteger s = null;
+ if (mu == 1)
+ {
+ s = u1;
+ }
+ else
+ {
+ // mu == -1
+ s = u1.Negate();
+ }
+
+ u2 = s.Subtract(u0.ShiftLeft(1));
+ u0 = u1;
+ u1 = u2;
+ // System.out.println(i + ": " + u2);
+ // System.out.println();
+ }
+
+ BigInteger[] retVal = {u0, u1};
+ return retVal;
+ }
+
+ /**
+ * Computes the auxiliary value tw
. If the width is
+ * 4, then for mu = 1
, tw = 6
and for
+ * mu = -1
, tw = 10
+ * @param mu The parameter μ
of the elliptic curve.
+ * @param w The window width of the WTNAF.
+ * @return the auxiliary value tw
+ */
+ public static BigInteger GetTw(sbyte mu, int w)
+ {
+ if (w == 4)
+ {
+ if (mu == 1)
+ {
+ return BigInteger.ValueOf(6);
+ }
+ else
+ {
+ // mu == -1
+ return BigInteger.ValueOf(10);
+ }
+ }
+ else
+ {
+ // For w <> 4, the values must be computed
+ BigInteger[] us = GetLucas(mu, w, false);
+ BigInteger twoToW = BigInteger.Zero.SetBit(w);
+ BigInteger u1invert = us[1].ModInverse(twoToW);
+ BigInteger tw;
+ tw = BigInteger.Two.Multiply(us[0]).Multiply(u1invert).Mod(twoToW);
+ //System.out.println("mu = " + mu);
+ //System.out.println("tw = " + tw);
+ return tw;
+ }
+ }
+
+ /**
+ * Computes the auxiliary values s0
and
+ * s1
used for partial modular reduction.
+ * @param curve The elliptic curve for which to compute
+ * s0
and s1
.
+ * @throws ArgumentException if curve
is not a
+ * Koblitz curve (Anomalous Binary Curve, ABC).
+ */
+ public static BigInteger[] GetSi(F2mCurve curve)
+ {
+ if (!curve.IsKoblitz)
+ throw new ArgumentException("si is defined for Koblitz curves only");
+
+ int m = curve.M;
+ int a = curve.A.ToBigInteger().IntValue;
+ sbyte mu = curve.GetMu();
+ int h = curve.H.IntValue;
+ int index = m + 3 - a;
+ BigInteger[] ui = GetLucas(mu, index, false);
+
+ BigInteger dividend0;
+ BigInteger dividend1;
+ if (mu == 1)
+ {
+ dividend0 = BigInteger.One.Subtract(ui[1]);
+ dividend1 = BigInteger.One.Subtract(ui[0]);
+ }
+ else if (mu == -1)
+ {
+ dividend0 = BigInteger.One.Add(ui[1]);
+ dividend1 = BigInteger.One.Add(ui[0]);
+ }
+ else
+ {
+ throw new ArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger[] si = new BigInteger[2];
+
+ if (h == 2)
+ {
+ si[0] = dividend0.ShiftRight(1);
+ si[1] = dividend1.ShiftRight(1).Negate();
+ }
+ else if (h == 4)
+ {
+ si[0] = dividend0.ShiftRight(2);
+ si[1] = dividend1.ShiftRight(2).Negate();
+ }
+ else
+ {
+ throw new ArgumentException("h (Cofactor) must be 2 or 4");
+ }
+
+ return si;
+ }
+
+ /**
+ * Partial modular reduction modulo
+ * (τm - 1)/(τ - 1)
.
+ * @param k The integer to be reduced.
+ * @param m The bitlength of the underlying finite field.
+ * @param a The parameter a
of the elliptic curve.
+ * @param s The auxiliary values s0
and
+ * s1
.
+ * @param mu The parameter μ of the elliptic curve.
+ * @param c The precision (number of bits of accuracy) of the partial
+ * modular reduction.
+ * @return ρ := k partmod (τm - 1)/(τ - 1)
+ */
+ public static ZTauElement PartModReduction(BigInteger k, int m, sbyte a,
+ BigInteger[] s, sbyte mu, sbyte c)
+ {
+ // d0 = s[0] + mu*s[1]; mu is either 1 or -1
+ BigInteger d0;
+ if (mu == 1)
+ {
+ d0 = s[0].Add(s[1]);
+ }
+ else
+ {
+ d0 = s[0].Subtract(s[1]);
+ }
+
+ BigInteger[] v = GetLucas(mu, m, true);
+ BigInteger vm = v[1];
+
+ SimpleBigDecimal lambda0 = ApproximateDivisionByN(
+ k, s[0], vm, a, m, c);
+
+ SimpleBigDecimal lambda1 = ApproximateDivisionByN(
+ k, s[1], vm, a, m, c);
+
+ ZTauElement q = Round(lambda0, lambda1, mu);
+
+ // r0 = n - d0*q0 - 2*s1*q1
+ BigInteger r0 = k.Subtract(d0.Multiply(q.u)).Subtract(
+ BigInteger.ValueOf(2).Multiply(s[1]).Multiply(q.v));
+
+ // r1 = s1*q0 - s0*q1
+ BigInteger r1 = s[1].Multiply(q.u).Subtract(s[0].Multiply(q.v));
+
+ return new ZTauElement(r0, r1);
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint}
+ * by a BigInteger
using the reduced τ
-adic
+ * NAF (RTNAF) method.
+ * @param p The F2mPoint to Multiply.
+ * @param k The BigInteger
by which to Multiply p
.
+ * @return k * p
+ */
+ public static F2mPoint MultiplyRTnaf(F2mPoint p, BigInteger k)
+ {
+ F2mCurve curve = (F2mCurve) p.Curve;
+ int m = curve.M;
+ sbyte a = (sbyte) curve.A.ToBigInteger().IntValue;
+ sbyte mu = curve.GetMu();
+ BigInteger[] s = curve.GetSi();
+ ZTauElement rho = PartModReduction(k, m, a, s, mu, (sbyte)10);
+
+ return MultiplyTnaf(p, rho);
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint}
+ * by an element λ
of Z[τ]
+ * using the τ
-adic NAF (TNAF) method.
+ * @param p The F2mPoint to Multiply.
+ * @param lambda The element λ
of
+ * Z[τ]
.
+ * @return λ * p
+ */
+ public static F2mPoint MultiplyTnaf(F2mPoint p, ZTauElement lambda)
+ {
+ F2mCurve curve = (F2mCurve)p.Curve;
+ sbyte mu = curve.GetMu();
+ sbyte[] u = TauAdicNaf(mu, lambda);
+
+ F2mPoint q = MultiplyFromTnaf(p, u);
+
+ return q;
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint}
+ * by an element λ
of Z[τ]
+ * using the τ
-adic NAF (TNAF) method, given the TNAF
+ * of λ
.
+ * @param p The F2mPoint to Multiply.
+ * @param u The the TNAF of λ
..
+ * @return λ * p
+ */
+ public static F2mPoint MultiplyFromTnaf(F2mPoint p, sbyte[] u)
+ {
+ F2mCurve curve = (F2mCurve)p.Curve;
+ F2mPoint q = (F2mPoint) curve.Infinity;
+ for (int i = u.Length - 1; i >= 0; i--)
+ {
+ q = Tau(q);
+ if (u[i] == 1)
+ {
+ q = (F2mPoint)q.AddSimple(p);
+ }
+ else if (u[i] == -1)
+ {
+ q = (F2mPoint)q.SubtractSimple(p);
+ }
+ }
+ return q;
+ }
+
+ /**
+ * Computes the [τ]
-adic window NAF of an element
+ * λ
of Z[τ]
.
+ * @param mu The parameter μ of the elliptic curve.
+ * @param lambda The element λ
of
+ * Z[τ]
of which to compute the
+ * [τ]
-adic NAF.
+ * @param width The window width of the resulting WNAF.
+ * @param pow2w 2width.
+ * @param tw The auxiliary value tw
.
+ * @param alpha The αu
's for the window width.
+ * @return The [τ]
-adic window NAF of
+ * λ
.
+ */
+ public static sbyte[] TauAdicWNaf(sbyte mu, ZTauElement lambda,
+ sbyte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ throw new ArgumentException("mu must be 1 or -1");
+
+ BigInteger norm = Norm(mu, lambda);
+
+ // Ceiling of log2 of the norm
+ int log2Norm = norm.BitLength;
+
+ // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+ int maxLength = log2Norm > 30 ? log2Norm + 4 + width : 34 + width;
+
+ // The array holding the TNAF
+ sbyte[] u = new sbyte[maxLength];
+
+ // 2^(width - 1)
+ BigInteger pow2wMin1 = pow2w.ShiftRight(1);
+
+ // Split lambda into two BigIntegers to simplify calculations
+ BigInteger r0 = lambda.u;
+ BigInteger r1 = lambda.v;
+ int i = 0;
+
+ // while lambda <> (0, 0)
+ while (!((r0.Equals(BigInteger.Zero))&&(r1.Equals(BigInteger.Zero))))
+ {
+ // if r0 is odd
+ if (r0.TestBit(0))
+ {
+ // uUnMod = r0 + r1*tw Mod 2^width
+ BigInteger uUnMod
+ = r0.Add(r1.Multiply(tw)).Mod(pow2w);
+
+ sbyte uLocal;
+ // if uUnMod >= 2^(width - 1)
+ if (uUnMod.CompareTo(pow2wMin1) >= 0)
+ {
+ uLocal = (sbyte) uUnMod.Subtract(pow2w).IntValue;
+ }
+ else
+ {
+ uLocal = (sbyte) uUnMod.IntValue;
+ }
+ // uLocal is now in [-2^(width-1), 2^(width-1)-1]
+
+ u[i] = uLocal;
+ bool s = true;
+ if (uLocal < 0)
+ {
+ s = false;
+ uLocal = (sbyte)-uLocal;
+ }
+ // uLocal is now >= 0
+
+ if (s)
+ {
+ r0 = r0.Subtract(alpha[uLocal].u);
+ r1 = r1.Subtract(alpha[uLocal].v);
+ }
+ else
+ {
+ r0 = r0.Add(alpha[uLocal].u);
+ r1 = r1.Add(alpha[uLocal].v);
+ }
+ }
+ else
+ {
+ u[i] = 0;
+ }
+
+ BigInteger t = r0;
+
+ if (mu == 1)
+ {
+ r0 = r1.Add(r0.ShiftRight(1));
+ }
+ else
+ {
+ // mu == -1
+ r0 = r1.Subtract(r0.ShiftRight(1));
+ }
+ r1 = t.ShiftRight(1).Negate();
+ i++;
+ }
+ return u;
+ }
+
+ /**
+ * Does the precomputation for WTNAF multiplication.
+ * @param p The ECPoint
for which to do the precomputation.
+ * @param a The parameter a
of the elliptic curve.
+ * @return The precomputation array for p
.
+ */
+ public static F2mPoint[] GetPreComp(F2mPoint p, sbyte a)
+ {
+ F2mPoint[] pu;
+ pu = new F2mPoint[16];
+ pu[1] = p;
+ sbyte[][] alphaTnaf;
+ if (a == 0)
+ {
+ alphaTnaf = Tnaf.Alpha0Tnaf;
+ }
+ else
+ {
+ // a == 1
+ alphaTnaf = Tnaf.Alpha1Tnaf;
+ }
+
+ int precompLen = alphaTnaf.Length;
+ for (int i = 3; i < precompLen; i = i + 2)
+ {
+ pu[i] = Tnaf.MultiplyFromTnaf(p, alphaTnaf[i]);
+ }
+
+ return pu;
+ }
+ }
+}
diff --git a/src/core/srcbc/math/ec/abc/ZTauElement.cs b/src/core/srcbc/math/ec/abc/ZTauElement.cs
new file mode 100644
index 0000000..3ed67ee
--- /dev/null
+++ b/src/core/srcbc/math/ec/abc/ZTauElement.cs
@@ -0,0 +1,36 @@
+namespace Org.BouncyCastle.Math.EC.Abc
+{
+ /**
+ * Class representing an element of Z[τ]
. Let
+ * λ
be an element of Z[τ]
. Then
+ * λ
is given as λ = u + vτ
. The
+ * components u
and v
may be used directly, there
+ * are no accessor methods.
+ * Immutable class.
+ */
+ internal class ZTauElement
+ {
+ /**
+ * The "real" part of λ
.
+ */
+ public readonly BigInteger u;
+
+ /**
+ * The "τ
-adic" part of λ
.
+ */
+ public readonly BigInteger v;
+
+ /**
+ * Constructor for an element λ
of
+ * Z[τ]
.
+ * @param u The "real" part of λ
.
+ * @param v The "τ
-adic" part of
+ * λ
.
+ */
+ public ZTauElement(BigInteger u, BigInteger v)
+ {
+ this.u = u;
+ this.v = v;
+ }
+ }
+}
diff --git a/src/core/srcbc/math/ec/multiplier/ECMultiplier.cs b/src/core/srcbc/math/ec/multiplier/ECMultiplier.cs
new file mode 100644
index 0000000..6affc37
--- /dev/null
+++ b/src/core/srcbc/math/ec/multiplier/ECMultiplier.cs
@@ -0,0 +1,18 @@
+namespace Org.BouncyCastle.Math.EC.Multiplier
+{
+ /**
+ * Interface for classes encapsulating a point multiplication algorithm
+ * for ECPoint
s.
+ */
+ internal interface ECMultiplier
+ {
+ /**
+ * Multiplies the ECPoint p
by k
, i.e.
+ * p
is added k
times to itself.
+ * @param p The ECPoint
to be multiplied.
+ * @param k The factor by which p
i multiplied.
+ * @return p
multiplied by k
.
+ */
+ ECPoint Multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo);
+ }
+}
diff --git a/src/core/srcbc/math/ec/multiplier/FpNafMultiplier.cs b/src/core/srcbc/math/ec/multiplier/FpNafMultiplier.cs
new file mode 100644
index 0000000..30f8242
--- /dev/null
+++ b/src/core/srcbc/math/ec/multiplier/FpNafMultiplier.cs
@@ -0,0 +1,39 @@
+namespace Org.BouncyCastle.Math.EC.Multiplier
+{
+ /**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm.
+ */
+ internal class FpNafMultiplier
+ : ECMultiplier
+ {
+ /**
+ * D.3.2 pg 101
+ * @see org.bouncycastle.math.ec.multiplier.ECMultiplier#multiply(org.bouncycastle.math.ec.ECPoint, java.math.BigInteger)
+ */
+ public ECPoint Multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo)
+ {
+ // TODO Probably should try to add this
+ // BigInteger e = k.Mod(n); // n == order of p
+ BigInteger e = k;
+ BigInteger h = e.Multiply(BigInteger.Three);
+
+ ECPoint neg = p.Negate();
+ ECPoint R = p;
+
+ for (int i = h.BitLength - 2; i > 0; --i)
+ {
+ R = R.Twice();
+
+ bool hBit = h.TestBit(i);
+ bool eBit = e.TestBit(i);
+
+ if (hBit != eBit)
+ {
+ R = R.Add(hBit ? p : neg);
+ }
+ }
+
+ return R;
+ }
+ }
+}
diff --git a/src/core/srcbc/math/ec/multiplier/PreCompInfo.cs b/src/core/srcbc/math/ec/multiplier/PreCompInfo.cs
new file mode 100644
index 0000000..77b90a2
--- /dev/null
+++ b/src/core/srcbc/math/ec/multiplier/PreCompInfo.cs
@@ -0,0 +1,11 @@
+namespace Org.BouncyCastle.Math.EC.Multiplier
+{
+ /**
+ * Interface for classes storing precomputation data for multiplication
+ * algorithms. Used as a Memento (see GOF patterns) for
+ * WNafMultiplier
.
+ */
+ internal interface PreCompInfo
+ {
+ }
+}
diff --git a/src/core/srcbc/math/ec/multiplier/ReferenceMultiplier.cs b/src/core/srcbc/math/ec/multiplier/ReferenceMultiplier.cs
new file mode 100644
index 0000000..16c8778
--- /dev/null
+++ b/src/core/srcbc/math/ec/multiplier/ReferenceMultiplier.cs
@@ -0,0 +1,30 @@
+namespace Org.BouncyCastle.Math.EC.Multiplier
+{
+ internal class ReferenceMultiplier
+ : ECMultiplier
+ {
+ /**
+ * Simple shift-and-add multiplication. Serves as reference implementation
+ * to verify (possibly faster) implementations in
+ * {@link org.bouncycastle.math.ec.ECPoint ECPoint}.
+ *
+ * @param p The point to multiply.
+ * @param k The factor by which to multiply.
+ * @return The result of the point multiplication k * p
.
+ */
+ public ECPoint Multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo)
+ {
+ ECPoint q = p.Curve.Infinity;
+ int t = k.BitLength;
+ for (int i = 0; i < t; i++)
+ {
+ if (k.TestBit(i))
+ {
+ q = q.Add(p);
+ }
+ p = p.Twice();
+ }
+ return q;
+ }
+ }
+}
diff --git a/src/core/srcbc/math/ec/multiplier/WNafMultiplier.cs b/src/core/srcbc/math/ec/multiplier/WNafMultiplier.cs
new file mode 100644
index 0000000..7a08169
--- /dev/null
+++ b/src/core/srcbc/math/ec/multiplier/WNafMultiplier.cs
@@ -0,0 +1,241 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Multiplier
+{
+ /**
+ * Class implementing the WNAF (Window Non-Adjacent Form) multiplication
+ * algorithm.
+ */
+ internal class WNafMultiplier
+ : ECMultiplier
+ {
+ /**
+ * Computes the Window NAF (non-adjacent Form) of an integer.
+ * @param width The width w
of the Window NAF. The width is
+ * defined as the minimal number w
, such that for any
+ * w
consecutive digits in the resulting representation, at
+ * most one is non-zero.
+ * @param k The integer of which the Window NAF is computed.
+ * @return The Window NAF of the given width, such that the following holds:
+ * k = −i=0l-1 ki2i
+ *
, where the ki
denote the elements of the
+ * returned sbyte[]
.
+ */
+ public sbyte[] WindowNaf(sbyte width, BigInteger k)
+ {
+ // The window NAF is at most 1 element longer than the binary
+ // representation of the integer k. sbyte can be used instead of short or
+ // int unless the window width is larger than 8. For larger width use
+ // short or int. However, a width of more than 8 is not efficient for
+ // m = log2(q) smaller than 2305 Bits. Note: Values for m larger than
+ // 1000 Bits are currently not used in practice.
+ sbyte[] wnaf = new sbyte[k.BitLength + 1];
+
+ // 2^width as short and BigInteger
+ short pow2wB = (short)(1 << width);
+ BigInteger pow2wBI = BigInteger.ValueOf(pow2wB);
+
+ int i = 0;
+
+ // The actual length of the WNAF
+ int length = 0;
+
+ // while k >= 1
+ while (k.SignValue > 0)
+ {
+ // if k is odd
+ if (k.TestBit(0))
+ {
+ // k Mod 2^width
+ BigInteger remainder = k.Mod(pow2wBI);
+
+ // if remainder > 2^(width - 1) - 1
+ if (remainder.TestBit(width - 1))
+ {
+ wnaf[i] = (sbyte)(remainder.IntValue - pow2wB);
+ }
+ else
+ {
+ wnaf[i] = (sbyte)remainder.IntValue;
+ }
+ // wnaf[i] is now in [-2^(width-1), 2^(width-1)-1]
+
+ k = k.Subtract(BigInteger.ValueOf(wnaf[i]));
+ length = i;
+ }
+ else
+ {
+ wnaf[i] = 0;
+ }
+
+ // k = k/2
+ k = k.ShiftRight(1);
+ i++;
+ }
+
+ length++;
+
+ // Reduce the WNAF array to its actual length
+ sbyte[] wnafShort = new sbyte[length];
+ Array.Copy(wnaf, 0, wnafShort, 0, length);
+ return wnafShort;
+ }
+
+ /**
+ * Multiplies this
by an integer k
using the
+ * Window NAF method.
+ * @param k The integer by which this
is multiplied.
+ * @return A new ECPoint
which equals this
+ * multiplied by k
.
+ */
+ public ECPoint Multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo)
+ {
+ WNafPreCompInfo wnafPreCompInfo;
+
+ if ((preCompInfo != null) && (preCompInfo is WNafPreCompInfo))
+ {
+ wnafPreCompInfo = (WNafPreCompInfo)preCompInfo;
+ }
+ else
+ {
+ // Ignore empty PreCompInfo or PreCompInfo of incorrect type
+ wnafPreCompInfo = new WNafPreCompInfo();
+ }
+
+ // floor(log2(k))
+ int m = k.BitLength;
+
+ // width of the Window NAF
+ sbyte width;
+
+ // Required length of precomputation array
+ int reqPreCompLen;
+
+ // Determine optimal width and corresponding length of precomputation
+ // array based on literature values
+ if (m < 13)
+ {
+ width = 2;
+ reqPreCompLen = 1;
+ }
+ else
+ {
+ if (m < 41)
+ {
+ width = 3;
+ reqPreCompLen = 2;
+ }
+ else
+ {
+ if (m < 121)
+ {
+ width = 4;
+ reqPreCompLen = 4;
+ }
+ else
+ {
+ if (m < 337)
+ {
+ width = 5;
+ reqPreCompLen = 8;
+ }
+ else
+ {
+ if (m < 897)
+ {
+ width = 6;
+ reqPreCompLen = 16;
+ }
+ else
+ {
+ if (m < 2305)
+ {
+ width = 7;
+ reqPreCompLen = 32;
+ }
+ else
+ {
+ width = 8;
+ reqPreCompLen = 127;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // The length of the precomputation array
+ int preCompLen = 1;
+
+ ECPoint[] preComp = wnafPreCompInfo.GetPreComp();
+ ECPoint twiceP = wnafPreCompInfo.GetTwiceP();
+
+ // Check if the precomputed ECPoints already exist
+ if (preComp == null)
+ {
+ // Precomputation must be performed from scratch, create an empty
+ // precomputation array of desired length
+ preComp = new ECPoint[]{ p };
+ }
+ else
+ {
+ // Take the already precomputed ECPoints to start with
+ preCompLen = preComp.Length;
+ }
+
+ if (twiceP == null)
+ {
+ // Compute twice(p)
+ twiceP = p.Twice();
+ }
+
+ if (preCompLen < reqPreCompLen)
+ {
+ // Precomputation array must be made bigger, copy existing preComp
+ // array into the larger new preComp array
+ ECPoint[] oldPreComp = preComp;
+ preComp = new ECPoint[reqPreCompLen];
+ Array.Copy(oldPreComp, 0, preComp, 0, preCompLen);
+
+ for (int i = preCompLen; i < reqPreCompLen; i++)
+ {
+ // Compute the new ECPoints for the precomputation array.
+ // The values 1, 3, 5, ..., 2^(width-1)-1 times p are
+ // computed
+ preComp[i] = twiceP.Add(preComp[i - 1]);
+ }
+ }
+
+ // Compute the Window NAF of the desired width
+ sbyte[] wnaf = WindowNaf(width, k);
+ int l = wnaf.Length;
+
+ // Apply the Window NAF to p using the precomputed ECPoint values.
+ ECPoint q = p.Curve.Infinity;
+ for (int i = l - 1; i >= 0; i--)
+ {
+ q = q.Twice();
+
+ if (wnaf[i] != 0)
+ {
+ if (wnaf[i] > 0)
+ {
+ q = q.Add(preComp[(wnaf[i] - 1)/2]);
+ }
+ else
+ {
+ // wnaf[i] < 0
+ q = q.Subtract(preComp[(-wnaf[i] - 1)/2]);
+ }
+ }
+ }
+
+ // Set PreCompInfo in ECPoint, such that it is available for next
+ // multiplication.
+ wnafPreCompInfo.SetPreComp(preComp);
+ wnafPreCompInfo.SetTwiceP(twiceP);
+ p.SetPreCompInfo(wnafPreCompInfo);
+ return q;
+ }
+ }
+}
diff --git a/src/core/srcbc/math/ec/multiplier/WNafPreCompInfo.cs b/src/core/srcbc/math/ec/multiplier/WNafPreCompInfo.cs
new file mode 100644
index 0000000..f7eed6c
--- /dev/null
+++ b/src/core/srcbc/math/ec/multiplier/WNafPreCompInfo.cs
@@ -0,0 +1,46 @@
+namespace Org.BouncyCastle.Math.EC.Multiplier
+{
+ /**
+ * Class holding precomputation data for the WNAF (Window Non-Adjacent Form)
+ * algorithm.
+ */
+ internal class WNafPreCompInfo
+ : PreCompInfo
+ {
+ /**
+ * Array holding the precomputed ECPoint
s used for the Window
+ * NAF multiplication in
+ * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply()
+ * WNafMultiplier.multiply()}
.
+ */
+ private ECPoint[] preComp = null;
+
+ /**
+ * Holds an ECPoint
representing twice(this). Used for the
+ * Window NAF multiplication in
+ * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply()
+ * WNafMultiplier.multiply()}
.
+ */
+ private ECPoint twiceP = null;
+
+ internal ECPoint[] GetPreComp()
+ {
+ return preComp;
+ }
+
+ internal void SetPreComp(ECPoint[] preComp)
+ {
+ this.preComp = preComp;
+ }
+
+ internal ECPoint GetTwiceP()
+ {
+ return twiceP;
+ }
+
+ internal void SetTwiceP(ECPoint twiceThis)
+ {
+ this.twiceP = twiceThis;
+ }
+ }
+}
diff --git a/src/core/srcbc/math/ec/multiplier/WTauNafMultiplier.cs b/src/core/srcbc/math/ec/multiplier/WTauNafMultiplier.cs
new file mode 100644
index 0000000..d005397
--- /dev/null
+++ b/src/core/srcbc/math/ec/multiplier/WTauNafMultiplier.cs
@@ -0,0 +1,120 @@
+using System;
+
+using Org.BouncyCastle.Math.EC.Abc;
+
+namespace Org.BouncyCastle.Math.EC.Multiplier
+{
+ /**
+ * Class implementing the WTNAF (Window
+ * τ
-adic Non-Adjacent Form) algorithm.
+ */
+ internal class WTauNafMultiplier
+ : ECMultiplier
+ {
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint}
+ * by k
using the reduced τ
-adic NAF (RTNAF)
+ * method.
+ * @param p The F2mPoint to multiply.
+ * @param k The integer by which to multiply k
.
+ * @return p
multiplied by k
.
+ */
+ public ECPoint Multiply(ECPoint point, BigInteger k, PreCompInfo preCompInfo)
+ {
+ if (!(point is F2mPoint))
+ throw new ArgumentException("Only F2mPoint can be used in WTauNafMultiplier");
+
+ F2mPoint p = (F2mPoint)point;
+
+ F2mCurve curve = (F2mCurve) p.Curve;
+ int m = curve.M;
+ sbyte a = (sbyte) curve.A.ToBigInteger().IntValue;
+ sbyte mu = curve.GetMu();
+ BigInteger[] s = curve.GetSi();
+
+ ZTauElement rho = Tnaf.PartModReduction(k, m, a, s, mu, (sbyte)10);
+
+ return MultiplyWTnaf(p, rho, preCompInfo, a, mu);
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint}
+ * by an element λ
of Z[τ]
using
+ * the τ
-adic NAF (TNAF) method.
+ * @param p The F2mPoint to multiply.
+ * @param lambda The element λ
of
+ * Z[τ]
of which to compute the
+ * [τ]
-adic NAF.
+ * @return p
multiplied by λ
.
+ */
+ private F2mPoint MultiplyWTnaf(F2mPoint p, ZTauElement lambda,
+ PreCompInfo preCompInfo, sbyte a, sbyte mu)
+ {
+ ZTauElement[] alpha;
+ if (a == 0)
+ {
+ alpha = Tnaf.Alpha0;
+ }
+ else
+ {
+ // a == 1
+ alpha = Tnaf.Alpha1;
+ }
+
+ BigInteger tw = Tnaf.GetTw(mu, Tnaf.Width);
+
+ sbyte[]u = Tnaf.TauAdicWNaf(mu, lambda, Tnaf.Width,
+ BigInteger.ValueOf(Tnaf.Pow2Width), tw, alpha);
+
+ return MultiplyFromWTnaf(p, u, preCompInfo);
+ }
+
+ /**
+ * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint}
+ * by an element λ
of Z[τ]
+ * using the window τ
-adic NAF (TNAF) method, given the
+ * WTNAF of λ
.
+ * @param p The F2mPoint to multiply.
+ * @param u The the WTNAF of λ
..
+ * @return λ * p
+ */
+ private static F2mPoint MultiplyFromWTnaf(F2mPoint p, sbyte[] u,
+ PreCompInfo preCompInfo)
+ {
+ F2mCurve curve = (F2mCurve)p.Curve;
+ sbyte a = (sbyte) curve.A.ToBigInteger().IntValue;
+
+ F2mPoint[] pu;
+ if ((preCompInfo == null) || !(preCompInfo is WTauNafPreCompInfo))
+ {
+ pu = Tnaf.GetPreComp(p, a);
+ p.SetPreCompInfo(new WTauNafPreCompInfo(pu));
+ }
+ else
+ {
+ pu = ((WTauNafPreCompInfo)preCompInfo).GetPreComp();
+ }
+
+ // q = infinity
+ F2mPoint q = (F2mPoint) p.Curve.Infinity;
+ for (int i = u.Length - 1; i >= 0; i--)
+ {
+ q = Tnaf.Tau(q);
+ if (u[i] != 0)
+ {
+ if (u[i] > 0)
+ {
+ q = q.AddSimple(pu[u[i]]);
+ }
+ else
+ {
+ // u[i] < 0
+ q = q.SubtractSimple(pu[-u[i]]);
+ }
+ }
+ }
+
+ return q;
+ }
+ }
+}
diff --git a/src/core/srcbc/math/ec/multiplier/WTauNafPreCompInfo.cs b/src/core/srcbc/math/ec/multiplier/WTauNafPreCompInfo.cs
new file mode 100644
index 0000000..07698f2
--- /dev/null
+++ b/src/core/srcbc/math/ec/multiplier/WTauNafPreCompInfo.cs
@@ -0,0 +1,41 @@
+namespace Org.BouncyCastle.Math.EC.Multiplier
+{
+ /**
+ * Class holding precomputation data for the WTNAF (Window
+ * τ
-adic Non-Adjacent Form) algorithm.
+ */
+ internal class WTauNafPreCompInfo
+ : PreCompInfo
+ {
+ /**
+ * Array holding the precomputed F2mPoint
s used for the
+ * WTNAF multiplication in
+ * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}
.
+ */
+ private readonly F2mPoint[] preComp;
+
+ /**
+ * Constructor for WTauNafPreCompInfo
+ * @param preComp Array holding the precomputed F2mPoint
s
+ * used for the WTNAF multiplication in
+ * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}
.
+ */
+ internal WTauNafPreCompInfo(F2mPoint[] preComp)
+ {
+ this.preComp = preComp;
+ }
+
+ /**
+ * @return the array holding the precomputed F2mPoint
s
+ * used for the WTNAF multiplication in
+ * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}
.
+ */
+ internal F2mPoint[] GetPreComp()
+ {
+ return preComp;
+ }
+ }
+}
diff --git a/src/core/srcbc/ocsp/BasicOCSPResp.cs b/src/core/srcbc/ocsp/BasicOCSPResp.cs
new file mode 100644
index 0000000..058a5fb
--- /dev/null
+++ b/src/core/srcbc/ocsp/BasicOCSPResp.cs
@@ -0,0 +1,215 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Ocsp;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Ocsp
+{
+ ///
+ /// BasicOcspResponse ::= SEQUENCE {
+ /// tbsResponseData ResponseData,
+ /// signatureAlgorithm AlgorithmIdentifier,
+ /// signature BIT STRING,
+ /// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
+ /// }
+ ///
+ /// + * OcspRequest ::= SEQUENCE { + * tbsRequest TBSRequest, + * optionalSignature [0] EXPLICIT Signature OPTIONAL } + * + * TBSRequest ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * requestorName [1] EXPLICIT GeneralName OPTIONAL, + * requestList SEQUENCE OF Request, + * requestExtensions [2] EXPLICIT Extensions OPTIONAL } + * + * Signature ::= SEQUENCE { + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL} + * + * Version ::= INTEGER { v1(0) } + * + * Request ::= SEQUENCE { + * reqCert CertID, + * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + * + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuers public key + * serialNumber CertificateSerialNumber } + *+ */ + public class OcspReq + : X509ExtensionBase + { + private OcspRequest req; + + public OcspReq( + OcspRequest req) + { + this.req = req; + } + + public OcspReq( + byte[] req) + : this(new Asn1InputStream(req)) + { + } + + public OcspReq( + Stream inStr) + : this(new Asn1InputStream(inStr)) + { + } + + private OcspReq( + Asn1InputStream aIn) + { + try + { + this.req = OcspRequest.GetInstance(aIn.ReadObject()); + } + catch (ArgumentException e) + { + throw new IOException("malformed request: " + e.Message); + } + catch (InvalidCastException e) + { + throw new IOException("malformed request: " + e.Message); + } + } + + /** + * Return the DER encoding of the tbsRequest field. + * @return DER encoding of tbsRequest + * @throws OcspException in the event of an encoding error. + */ + public byte[] GetTbsRequest() + { + try + { + return req.TbsRequest.GetEncoded(); + } + catch (IOException e) + { + throw new OcspException("problem encoding tbsRequest", e); + } + } + + public int Version + { + get { return req.TbsRequest.Version.Value.IntValue + 1; } + } + + public GeneralName RequestorName + { + get { return GeneralName.GetInstance(req.TbsRequest.RequestorName); } + } + + public Req[] GetRequestList() + { + Asn1Sequence seq = req.TbsRequest.RequestList; + Req[] requests = new Req[seq.Count]; + + for (int i = 0; i != requests.Length; i++) + { + requests[i] = new Req(Request.GetInstance(seq[i])); + } + + return requests; + } + + public X509Extensions RequestExtensions + { + get { return X509Extensions.GetInstance(req.TbsRequest.RequestExtensions); } + } + + protected override X509Extensions GetX509Extensions() + { + return RequestExtensions; + } + + /** + * return the object identifier representing the signature algorithm + */ + public string SignatureAlgOid + { + get + { + if (!this.IsSigned) + return null; + + return req.OptionalSignature.SignatureAlgorithm.ObjectID.Id; + } + } + + public byte[] GetSignature() + { + if (!this.IsSigned) + return null; + + return req.OptionalSignature.SignatureValue.GetBytes(); + } + + private ArrayList GetCertList() + { + // load the certificates if we have any + + ArrayList certs = new ArrayList(); + Asn1Sequence s = req.OptionalSignature.Certs; + + if (s != null) + { + foreach (Asn1Encodable ae in s) + { + try + { + certs.Add(new X509CertificateParser().ReadCertificate(ae.GetEncoded())); + } + catch (Exception e) + { + throw new OcspException("can't re-encode certificate!", e); + } + } + } + + return certs; + } + + public X509Certificate[] GetCerts() + { + if (!this.IsSigned) + return null; + + ArrayList certs = this.GetCertList(); + + return (X509Certificate[]) certs.ToArray(typeof(X509Certificate)); + } + + /** + * If the request is signed return a possibly empty CertStore containing the certificates in the + * request. If the request is not signed the method returns null. + * + * @return null if not signed, a CertStore otherwise + * @throws OcspException + */ + public IX509Store GetCertificates( + string type) + { + if (!this.IsSigned) + return null; + + try + { + return X509StoreFactory.Create( + "Certificate/" + type, + new X509CollectionStoreParameters(this.GetCertList())); + } + catch (Exception e) + { + throw new OcspException("can't setup the CertStore", e); + } + } + + /** + * Return whether or not this request is signed. + * + * @return true if signed false otherwise. + */ + public bool IsSigned + { + get { return req.OptionalSignature != null; } + } + + /** + * Verify the signature against the TBSRequest object we contain. + */ + public bool Verify( + AsymmetricKeyParameter publicKey) + { + if (!this.IsSigned) + throw new OcspException("attempt to Verify signature on unsigned object"); + + try + { + ISigner signature = SignerUtilities.GetSigner(this.SignatureAlgOid); + + signature.Init(false, publicKey); + + byte[] encoded = req.TbsRequest.GetEncoded(); + + signature.BlockUpdate(encoded, 0, encoded.Length); + + return signature.VerifySignature(this.GetSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing sig: " + e, e); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return req.GetEncoded(); + } + } +} diff --git a/src/core/srcbc/ocsp/OCSPReqGenerator.cs b/src/core/srcbc/ocsp/OCSPReqGenerator.cs new file mode 100644 index 0000000..62bb13a --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPReqGenerator.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class OcspReqGenerator + { + private IList list = new ArrayList(); + private GeneralName requestorName = null; + private X509Extensions requestExtensions = null; + + private class RequestObject + { + internal CertificateID certId; + internal X509Extensions extensions; + + public RequestObject( + CertificateID certId, + X509Extensions extensions) + { + this.certId = certId; + this.extensions = extensions; + } + + public Request ToRequest() + { + return new Request(certId.ToAsn1Object(), extensions); + } + } + + /** + * Add a request for the given CertificateID. + * + * @param certId certificate ID of interest + */ + public void AddRequest( + CertificateID certId) + { + list.Add(new RequestObject(certId, null)); + } + + /** + * Add a request with extensions + * + * @param certId certificate ID of interest + * @param singleRequestExtensions the extensions to attach to the request + */ + public void AddRequest( + CertificateID certId, + X509Extensions singleRequestExtensions) + { + list.Add(new RequestObject(certId, singleRequestExtensions)); + } + + /** + * Set the requestor name to the passed in X509Principal + * + * @param requestorName a X509Principal representing the requestor name. + */ + public void SetRequestorName( + X509Name requestorName) + { + try + { + this.requestorName = new GeneralName(GeneralName.DirectoryName, requestorName); + } + catch (Exception e) + { + throw new ArgumentException("cannot encode principal", e); + } + } + + public void SetRequestorName( + GeneralName requestorName) + { + this.requestorName = requestorName; + } + + public void SetRequestExtensions( + X509Extensions requestExtensions) + { + this.requestExtensions = requestExtensions; + } + + private OcspReq GenerateRequest( + DerObjectIdentifier signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + SecureRandom random) + { + Asn1EncodableVector requests = new Asn1EncodableVector(); + + foreach (RequestObject reqObj in list) + { + try + { + requests.Add(reqObj.ToRequest()); + } + catch (Exception e) + { + throw new OcspException("exception creating Request", e); + } + } + + TbsRequest tbsReq = new TbsRequest(requestorName, new DerSequence(requests), requestExtensions); + + ISigner sig = null; + Signature signature = null; + + if (signingAlgorithm != null) + { + if (requestorName == null) + { + throw new OcspException("requestorName must be specified if request is signed."); + } + + try + { + sig = SignerUtilities.GetSigner(signingAlgorithm.Id); + if (random != null) + { + sig.Init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.Init(true, privateKey); + } + } + catch (Exception e) + { + throw new OcspException("exception creating signature: " + e, e); + } + + DerBitString bitSig = null; + + try + { + byte[] encoded = tbsReq.GetEncoded(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + bitSig = new DerBitString(sig.GenerateSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing TBSRequest: " + e, e); + } + + AlgorithmIdentifier sigAlgId = new AlgorithmIdentifier(signingAlgorithm, DerNull.Instance); + + if (chain != null && chain.Length > 0) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + try + { + for (int i = 0; i != chain.Length; i++) + { + v.Add( + X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(chain[i].GetEncoded()))); + } + } + catch (IOException e) + { + throw new OcspException("error processing certs", e); + } + catch (CertificateEncodingException e) + { + throw new OcspException("error encoding certs", e); + } + + signature = new Signature(sigAlgId, bitSig, new DerSequence(v)); + } + else + { + signature = new Signature(sigAlgId, bitSig); + } + } + + return new OcspReq(new OcspRequest(tbsReq, signature)); + } + + /** + * Generate an unsigned request + * + * @return the OcspReq + * @throws OcspException + */ + public OcspReq Generate() + { + return GenerateRequest(null, null, null, null); + } + + public OcspReq Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain) + { + return Generate(signingAlgorithm, privateKey, chain, null); + } + + public OcspReq Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + SecureRandom random) + { + if (signingAlgorithm == null) + throw new ArgumentException("no signing algorithm specified"); + + try + { + DerObjectIdentifier oid = OcspUtilities.GetAlgorithmOid(signingAlgorithm); + + return GenerateRequest(oid, privateKey, chain, random); + } + catch (ArgumentException) + { + throw new ArgumentException("unknown signing algorithm specified: " + signingAlgorithm); + } + } + + /** + * Return an IEnumerable of the signature names supported by the generator. + * + * @return an IEnumerable containing recognised names. + */ + public IEnumerable SignatureAlgNames + { + get { return OcspUtilities.AlgNames; } + } + } +} diff --git a/src/core/srcbc/ocsp/OCSPResp.cs b/src/core/srcbc/ocsp/OCSPResp.cs new file mode 100644 index 0000000..13e3f72 --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPResp.cs @@ -0,0 +1,100 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; + +namespace Org.BouncyCastle.Ocsp +{ + public class OcspResp + { + private OcspResponse resp; + + public OcspResp( + OcspResponse resp) + { + this.resp = resp; + } + + public OcspResp( + byte[] resp) + : this(new Asn1InputStream(resp)) + { + } + + public OcspResp( + Stream inStr) + : this(new Asn1InputStream(inStr)) + { + } + + private OcspResp( + Asn1InputStream aIn) + { + try + { + this.resp = OcspResponse.GetInstance(aIn.ReadObject()); + } + catch (Exception e) + { + throw new IOException("malformed response: " + e.Message, e); + } + } + + public int Status + { + get { return this.resp.ResponseStatus.Value.IntValue; } + } + + public object GetResponseObject() + { + ResponseBytes rb = this.resp.ResponseBytes; + + if (rb == null) + return null; + + if (rb.ResponseType.Equals(OcspObjectIdentifiers.PkixOcspBasic)) + { + try + { + return new BasicOcspResp( + BasicOcspResponse.GetInstance( + Asn1Object.FromByteArray(rb.Response.GetOctets()))); + } + catch (Exception e) + { + throw new OcspException("problem decoding object: " + e, e); + } + } + + return rb.Response; + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return resp.GetEncoded(); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + OcspResp other = obj as OcspResp; + + if (other == null) + return false; + + return resp.Equals(other.resp); + } + + public override int GetHashCode() + { + return resp.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/ocsp/OCSPRespGenerator.cs b/src/core/srcbc/ocsp/OCSPRespGenerator.cs new file mode 100644 index 0000000..61d6f5f --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPRespGenerator.cs @@ -0,0 +1,54 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * base generator for an OCSP response - at the moment this only supports the + * generation of responses containing BasicOCSP responses. + */ + public class OCSPRespGenerator + { + public const int Successful = 0; // Response has valid confirmations + public const int MalformedRequest = 1; // Illegal confirmation request + public const int InternalError = 2; // Internal error in issuer + public const int TryLater = 3; // Try again later + // (4) is not used + public const int SigRequired = 5; // Must sign the request + public const int Unauthorized = 6; // Request unauthorized + + public OcspResp Generate( + int status, + object response) + { + if (response == null) + { + return new OcspResp(new OcspResponse(new OcspResponseStatus(status),null)); + } + if (response is BasicOcspResp) + { + BasicOcspResp r = (BasicOcspResp)response; + Asn1OctetString octs; + + try + { + octs = new DerOctetString(r.GetEncoded()); + } + catch (Exception e) + { + throw new OcspException("can't encode object.", e); + } + + ResponseBytes rb = new ResponseBytes( + OcspObjectIdentifiers.PkixOcspBasic, octs); + + return new OcspResp(new OcspResponse( + new OcspResponseStatus(status), rb)); + } + + throw new OcspException("unknown response object"); + } + } +} diff --git a/src/core/srcbc/ocsp/OCSPRespStatus.cs b/src/core/srcbc/ocsp/OCSPRespStatus.cs new file mode 100644 index 0000000..bef915e --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPRespStatus.cs @@ -0,0 +1,22 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + [Obsolete("Use version with correct spelling 'OcspRespStatus'")] + public abstract class OcscpRespStatus : OcspRespStatus + { + } + + public abstract class OcspRespStatus + { + /** + * note 4 is not used. + */ + public const int Successful = 0; // --Response has valid confirmations + public const int MalformedRequest = 1; // --Illegal confirmation request + public const int InternalError = 2; // --Internal error in issuer + public const int TryLater = 3; // --Try again later + public const int SigRequired = 5; // --Must sign the request + public const int Unauthorized = 6; // --Request unauthorized + } +} diff --git a/src/core/srcbc/ocsp/OCSPUtil.cs b/src/core/srcbc/ocsp/OCSPUtil.cs new file mode 100644 index 0000000..8704c74 --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPUtil.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Ocsp +{ + class OcspUtilities + { + private static readonly Hashtable algorithms = new Hashtable(); + private static readonly Hashtable oids = new Hashtable(); + private static readonly ISet noParams = new HashSet(); + + static OcspUtilities() + { + algorithms.Add("MD2WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD2WITHRSA", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD5WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("MD5WITHRSA", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("SHA1WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA1WITHRSA", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("SHA1WITHDSA", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("DSAWITHSHA1", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224); + algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256); + algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224); + algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256); + algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384); + algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512); + algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + + oids.Add(PkcsObjectIdentifiers.MD2WithRsaEncryption, "MD2WITHRSA"); + oids.Add(PkcsObjectIdentifiers.MD5WithRsaEncryption, "MD5WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha1WithRsaEncryption, "SHA1WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512WITHRSA"); + oids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160, "RIPEMD160WITHRSA"); + oids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128, "RIPEMD128WITHRSA"); + oids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256, "RIPEMD256WITHRSA"); + oids.Add(X9ObjectIdentifiers.IdDsaWithSha1, "SHA1WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha224, "SHA224WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha256, "SHA256WITHDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512WITHECDSA"); + oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, "GOST3411WITHGOST3410"); + + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512); + noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1); + noParams.Add(NistObjectIdentifiers.DsaWithSha224); + noParams.Add(NistObjectIdentifiers.DsaWithSha256); + } + + internal static DerObjectIdentifier GetAlgorithmOid( + string algorithmName) + { + algorithmName = algorithmName.ToUpper(CultureInfo.InvariantCulture); + + if (algorithms.ContainsKey(algorithmName)) + { + return (DerObjectIdentifier)algorithms[algorithmName]; + } + + return new DerObjectIdentifier(algorithmName); + } + + + internal static string GetAlgorithmName( + DerObjectIdentifier oid) + { + if (oids.ContainsKey(oid)) + { + return (string)oids[oid]; + } + + return oid.Id; + } + + internal static AlgorithmIdentifier GetSigAlgID( + DerObjectIdentifier sigOid) + { + if (noParams.Contains(sigOid)) + { + return new AlgorithmIdentifier(sigOid); + } + + return new AlgorithmIdentifier(sigOid, DerNull.Instance); + } + + internal static IEnumerable AlgNames + { + get { return new EnumerableProxy(algorithms.Keys); } + } + } +} diff --git a/src/core/srcbc/ocsp/Req.cs b/src/core/srcbc/ocsp/Req.cs new file mode 100644 index 0000000..df66e0a --- /dev/null +++ b/src/core/srcbc/ocsp/Req.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class Req + : X509ExtensionBase + { + private Request req; + + public Req( + Request req) + { + this.req = req; + } + + public CertificateID GetCertID() + { + return new CertificateID(req.ReqCert); + } + + public X509Extensions SingleRequestExtensions + { + get { return req.SingleRequestExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return SingleRequestExtensions; + } + } +} diff --git a/src/core/srcbc/ocsp/RespData.cs b/src/core/srcbc/ocsp/RespData.cs new file mode 100644 index 0000000..f1ee2e9 --- /dev/null +++ b/src/core/srcbc/ocsp/RespData.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class RespData + : X509ExtensionBase + { + internal readonly ResponseData data; + + public RespData( + ResponseData data) + { + this.data = data; + } + + public int Version + { + get { return data.Version.Value.IntValue + 1; } + } + + public RespID GetResponderId() + { + return new RespID(data.ResponderID); + } + + public DateTime ProducedAt + { + get { return data.ProducedAt.ToDateTime(); } + } + + public SingleResp[] GetResponses() + { + Asn1Sequence s = data.Responses; + SingleResp[] rs = new SingleResp[s.Count]; + + for (int i = 0; i != rs.Length; i++) + { + rs[i] = new SingleResp(SingleResponse.GetInstance(s[i])); + } + + return rs; + } + + public X509Extensions ResponseExtensions + { + get { return data.ResponseExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return ResponseExtensions; + } + } +} diff --git a/src/core/srcbc/ocsp/RespID.cs b/src/core/srcbc/ocsp/RespID.cs new file mode 100644 index 0000000..56018a8 --- /dev/null +++ b/src/core/srcbc/ocsp/RespID.cs @@ -0,0 +1,86 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * Carrier for a ResponderID. + */ + public class RespID + { + internal readonly ResponderID id; + + public RespID( + ResponderID id) + { + this.id = id; + } + + public RespID( + X509Name name) + { + try + { + this.id = new ResponderID(name); + } + catch (Exception e) + { + throw new ArgumentException("can't decode name.", e); + } + } + + public RespID( + AsymmetricKeyParameter publicKey) + { + try + { + IDigest digest = DigestUtilities.GetDigest("SHA1"); + + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey); + + byte[] encoded = info.PublicKeyData.GetBytes(); + digest.BlockUpdate(encoded, 0, encoded.Length); + + byte[] hash = DigestUtilities.DoFinal(digest); + + Asn1OctetString keyHash = new DerOctetString(hash); + + this.id = new ResponderID(keyHash); + } + catch (Exception e) + { + throw new OcspException("problem creating ID: " + e, e); + } + } + + public ResponderID ToAsn1Object() + { + return id; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + RespID other = obj as RespID; + + if (other == null) + return false; + + return id.Equals(other.id); + } + + public override int GetHashCode() + { + return id.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/ocsp/RevokedStatus.cs b/src/core/srcbc/ocsp/RevokedStatus.cs new file mode 100644 index 0000000..07925af --- /dev/null +++ b/src/core/srcbc/ocsp/RevokedStatus.cs @@ -0,0 +1,58 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * wrapper for the RevokedInfo object + */ + public class RevokedStatus + : CertificateStatus + { + internal readonly RevokedInfo info; + + public RevokedStatus( + RevokedInfo info) + { + this.info = info; + } + + public RevokedStatus( + DateTime revocationDate, + int reason) + { + this.info = new RevokedInfo(new DerGeneralizedTime(revocationDate), new CrlReason(reason)); + } + + public DateTime RevocationTime + { + get { return info.RevocationTime.ToDateTime(); } + } + + public bool HasRevocationReason + { + get { return (info.RevocationReason != null); } + } + + /** + * return the revocation reason. Note: this field is optional, test for it + * with hasRevocationReason() first. + * @exception InvalidOperationException if a reason is asked for and none is avaliable + */ + public int RevocationReason + { + get + { + if (info.RevocationReason == null) + { + throw new InvalidOperationException("attempt to get a reason where none is available"); + } + + return info.RevocationReason.Value.IntValue; + } + } + } +} diff --git a/src/core/srcbc/ocsp/SingleResp.cs b/src/core/srcbc/ocsp/SingleResp.cs new file mode 100644 index 0000000..c02390e --- /dev/null +++ b/src/core/srcbc/ocsp/SingleResp.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class SingleResp + : X509ExtensionBase + { + internal readonly SingleResponse resp; + + public SingleResp( + SingleResponse resp) + { + this.resp = resp; + } + + public CertificateID GetCertID() + { + return new CertificateID(resp.CertId); + } + + /** + * Return the status object for the response - null indicates good. + * + * @return the status object for the response, null if it is good. + */ + public object GetCertStatus() + { + CertStatus s = resp.CertStatus; + + if (s.TagNo == 0) + { + return null; // good + } + + if (s.TagNo == 1) + { + return new RevokedStatus(RevokedInfo.GetInstance(s.Status)); + } + + return new UnknownStatus(); + } + + public DateTime ThisUpdate + { + get { return resp.ThisUpdate.ToDateTime(); } + } + + /** + * return the NextUpdate value - note: this is an optional field so may + * be returned as null. + * + * @return nextUpdate, or null if not present. + */ + public DateTimeObject NextUpdate + { + get + { + return resp.NextUpdate == null + ? null + : new DateTimeObject(resp.NextUpdate.ToDateTime()); + } + } + + public X509Extensions SingleExtensions + { + get { return resp.SingleExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return SingleExtensions; + } + } +} diff --git a/src/core/srcbc/ocsp/UnknownStatus.cs b/src/core/srcbc/ocsp/UnknownStatus.cs new file mode 100644 index 0000000..e120b48 --- /dev/null +++ b/src/core/srcbc/ocsp/UnknownStatus.cs @@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * wrapper for the UnknownInfo object + */ + public class UnknownStatus + : CertificateStatus + { + public UnknownStatus() + { + } + } +} diff --git a/src/core/srcbc/openpgp/IStreamGenerator.cs b/src/core/srcbc/openpgp/IStreamGenerator.cs new file mode 100644 index 0000000..ebaf7ff --- /dev/null +++ b/src/core/srcbc/openpgp/IStreamGenerator.cs @@ -0,0 +1,7 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public interface IStreamGenerator + { + void Close(); + } +} diff --git a/src/core/srcbc/openpgp/PGPKeyRing.cs b/src/core/srcbc/openpgp/PGPKeyRing.cs new file mode 100644 index 0000000..a919e22 --- /dev/null +++ b/src/core/srcbc/openpgp/PGPKeyRing.cs @@ -0,0 +1,77 @@ +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public abstract class PgpKeyRing + : PgpObject + { + internal PgpKeyRing() + { + } + + internal static TrustPacket ReadOptionalTrustPacket( + BcpgInputStream bcpgInput) + { + return (bcpgInput.NextPacketTag() == PacketTag.Trust) + ? (TrustPacket) bcpgInput.ReadPacket() + : null; + } + + internal static ArrayList ReadSignaturesAndTrust( + BcpgInputStream bcpgInput) + { + try + { + ArrayList sigList = new ArrayList(); + + while (bcpgInput.NextPacketTag() == PacketTag.Signature) + { + SignaturePacket signaturePacket = (SignaturePacket) bcpgInput.ReadPacket(); + TrustPacket trustPacket = ReadOptionalTrustPacket(bcpgInput); + + sigList.Add(new PgpSignature(signaturePacket, trustPacket)); + } + + return sigList; + } + catch (PgpException e) + { + throw new IOException("can't create signature object: " + e.Message, e); + } + } + + internal static void ReadUserIDs( + BcpgInputStream bcpgInput, + out ArrayList ids, + out ArrayList idTrusts, + out ArrayList idSigs) + { + ids = new ArrayList(); + idTrusts = new ArrayList(); + idSigs = new ArrayList(); + + while (bcpgInput.NextPacketTag() == PacketTag.UserId + || bcpgInput.NextPacketTag() == PacketTag.UserAttribute) + { + Packet obj = bcpgInput.ReadPacket(); + if (obj is UserIdPacket) + { + UserIdPacket id = (UserIdPacket)obj; + ids.Add(id.GetId()); + } + else + { + UserAttributePacket user = (UserAttributePacket) obj; + ids.Add(new PgpUserAttributeSubpacketVector(user.GetSubpackets())); + } + + idTrusts.Add( + ReadOptionalTrustPacket(bcpgInput)); + + idSigs.Add( + ReadSignaturesAndTrust(bcpgInput)); + } + } + } +} diff --git a/src/core/srcbc/openpgp/PGPObject.cs b/src/core/srcbc/openpgp/PGPObject.cs new file mode 100644 index 0000000..624f8d0 --- /dev/null +++ b/src/core/srcbc/openpgp/PGPObject.cs @@ -0,0 +1,9 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public abstract class PgpObject + { + internal PgpObject() + { + } + } +} diff --git a/src/core/srcbc/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs b/src/core/srcbc/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs new file mode 100644 index 0000000..e8fc515 --- /dev/null +++ b/src/core/srcbc/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Bcpg.Attr; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public class PgpUserAttributeSubpacketVectorGenerator + { + private ArrayList list = new ArrayList(); + + public virtual void SetImageAttribute( + ImageAttrib.Format imageType, + byte[] imageData) + { + if (imageData == null) + throw new ArgumentException("attempt to set null image", "imageData"); + + list.Add(new ImageAttrib(imageType, imageData)); + } + + public virtual PgpUserAttributeSubpacketVector Generate() + { + return new PgpUserAttributeSubpacketVector( + (UserAttributeSubpacket[]) list.ToArray(typeof(UserAttributeSubpacket))); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpCompressedData.cs b/src/core/srcbc/openpgp/PgpCompressedData.cs new file mode 100644 index 0000000..5fbfaff --- /dev/null +++ b/src/core/srcbc/openpgp/PgpCompressedData.cs @@ -0,0 +1,50 @@ +using System.IO; + +using Org.BouncyCastle.Apache.Bzip2; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + ///
+ /// Return an output stream which will save the data being written to + /// the compressed object. + ///
+ ///
+ /// The stream created can be closed off by either calling Close()
+ /// on the stream or Close() on the generator. Closing the returned
+ /// stream does not close off the Stream parameter
+ /// Return an output stream which will compress the data as it is written to it. + /// The stream will be written out in chunks according to the size of the passed in buffer. + ///
+ ///
+ /// The stream created can be closed off by either calling Close()
+ /// on the stream or Close() on the generator. Closing the returned
+ /// stream does not close off the Stream parameter
+ /// Note: if the buffer is not a power of 2 in length only the largest power of 2 + /// bytes worth of the buffer will be used. + ///
+ ///+ /// Note: using this may break compatibility with RFC 1991 compliant tools. + /// Only recent OpenPGP implementations are capable of accepting these streams. + ///
+ ///+ /// If buffer is non null stream assumed to be partial, otherwise the length will be used + /// to output a fixed length packet. + ///
+ ///
+ /// The stream created can be closed off by either calling Close()
+ /// on the stream or Close() on the generator. Closing the returned
+ /// stream does not close off the Stream parameter
+ /// Return an output stream which will encrypt the data as it is written to it. + ///
+ ///
+ /// The stream created can be closed off by either calling Close()
+ /// on the stream or Close() on the generator. Closing the returned
+ /// stream does not close off the Stream parameter
+ /// Return an output stream which will encrypt the data as it is written to it. + /// The stream will be written out in chunks according to the size of the passed in buffer. + ///
+ ///
+ /// The stream created can be closed off by either calling Close()
+ /// on the stream or Close() on the generator. Closing the returned
+ /// stream does not close off the Stream parameter
+ /// Note: if the buffer is not a power of 2 in length only the largest power of 2 + /// bytes worth of the buffer will be used. + ///
+ ///+ /// Close off the encrypted object - this is equivalent to calling Close() on the stream + /// returned by the Open() method. + ///
+ ///+ /// Note: This does not close the underlying output stream, only the stream on top of + /// it created by the Open() method. + ///
+ ///+ /// A word for the unwary, the KeyId for an OpenPGP public key is calculated from + /// a hash that includes the time of creation, if you pass a different date to the + /// constructor below with the same public private key pair the KeyIs will not be the + /// same as for previous generations of the key, so ideally you only want to do + /// this once. + ///
+ ///+ /// Open a literal data packet, returning a stream to store the data inside the packet. + ///
+ ///
+ /// The stream created can be closed off by either calling Close()
+ /// on the stream or Close() on the generator. Closing the returned
+ /// stream does not close off the Stream parameter
+ /// Open a literal data packet, returning a stream to store the data inside the packet, + /// as an indefinite length stream. The stream is written out as a series of partial + /// packets with a chunk size determined by the size of the passed in buffer. + ///
+ ///
+ /// The stream created can be closed off by either calling Close()
+ /// on the stream or Close() on the generator. Closing the returned
+ /// stream does not close off the Stream parameter
+ /// Note: if the buffer is not a power of 2 in length only the largest power of 2 + /// bytes worth of the buffer will be used.
+ ///
+ /// Open a literal data packet for the passed in
+ /// The stream created can be closed off by either calling Close()
+ /// on the stream or Close() on the generator. Closing the returned
+ /// stream does not close off the Stream parameter
+ /// Note: if this class finds a PgpPublicKey or a PgpSecretKey it + /// will create a PgpPublicKeyRing, or a PgpSecretKeyRing for each + /// key found. If all you are trying to do is read a key ring file use + /// either PgpPublicKeyRingBundle or PgpSecretKeyRingBundle.
+ ///
+ /// Often PGP keyring files consist of multiple master keys, if you are trying to process
+ /// or construct one of these you should use the
+ /// Often PGP keyring files consist of multiple master keys, if you are trying to process
+ /// or construct one of these you should use the
+ /// Note: this overrides the generation of a creation time when the signature + /// is generated.
+ ///+ * In the case of PKCS7 objects the reader will return a CMS ContentInfo object. Keys and + * Certificates will be returned using the appropriate java.security type.
+ */ + public class PemReader + { + private readonly TextReader reader; + private readonly IPasswordFinder pFinder; + + public TextReader Reader + { + get { return reader; } + } + + /** + * Create a new PemReader + * + * @param reader the Reader + */ + public PemReader( + TextReader reader) + : this(reader, null) + { + } + + /** + * Create a new PemReader with a password finder + * + * @param reader the Reader + * @param pFinder the password finder + */ + public PemReader( + TextReader reader, + IPasswordFinder pFinder) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + this.reader = reader; + this.pFinder = pFinder; + } + + private const string BeginString = "-----BEGIN "; + + public object ReadObject() + { + string line; + while ((line = reader.ReadLine()) != null) + { + int startPos = line.IndexOf(BeginString); + if (startPos == -1) + continue; + + startPos += BeginString.Length; + + int endPos = line.IndexOf('-', startPos); + if (endPos == -1) + endPos = line.Length; + + string headerName = line.Substring(startPos, endPos - startPos).Trim(); + //Console.WriteLine("[" + headerName + "]"); + + string endMarker = "-----END " + headerName; + + switch (headerName) + { + case "PUBLIC KEY": + return ReadPublicKey(endMarker); + case "RSA PUBLIC KEY": + return ReadRsaPublicKey(endMarker); + case "CERTIFICATE REQUEST": + case "NEW CERTIFICATE REQUEST": + return ReadCertificateRequest(endMarker); + case "CERTIFICATE": + case "X509 CERTIFICATE": + return ReadCertificate(endMarker); + case "PKCS7": + return ReadPkcs7(endMarker); + case "X509 CRL": + return ReadCrl(endMarker); + case "ATTRIBUTE CERTIFICATE": + return ReadAttributeCertificate(endMarker); + case "RSA PRIVATE KEY": + return ReadKeyPair("RSA", endMarker); + case "DSA PRIVATE KEY": + return ReadKeyPair("DSA", endMarker); + // TODO Add back in when tests done, and return type issue resolved + //case "EC PARAMETERS": + // return ReadECParameters(endMarker); + case "EC PRIVATE KEY": + return ReadECPrivateKey(endMarker); + default: + // TODO Throw an exception for an unknown header? + + // Ignore contents + ReadBytes(endMarker); + break; + } + } + + return null; + } + + private byte[] ReadBytes( + string endMarker) + { + return ReadBytesAndFields(endMarker, null); + } + + private byte[] ReadBytesAndFields( + string endMarker, + IDictionary fields) + { + StringBuilder buf = new StringBuilder(); + + string line; + while ((line = reader.ReadLine()) != null + && line.IndexOf(endMarker) == -1) + { + int colonPos = line.IndexOf(':'); + + if (colonPos == -1) + { + buf.Append(line.Trim()); + } + else if (fields != null) + { + // Process field + string fieldName = line.Substring(0, colonPos).Trim(); + + if (fieldName.StartsWith("X-")) + fieldName = fieldName.Substring(2); + + string fieldValue = line.Substring(colonPos + 1).Trim(); + + // TODO Complain if field already specified? + fields[fieldName] = fieldValue; + } + } + + if (line == null) + { + throw new IOException(endMarker + " not found"); + } + + if (buf.Length % 4 != 0) + { + throw new IOException("base64 data appears to be truncated"); + } + + return Base64.Decode(buf.ToString()); + } + + private AsymmetricKeyParameter ReadRsaPublicKey( + string endMarker) + { + RsaPublicKeyStructure rsaPubStructure = RsaPublicKeyStructure.GetInstance( + Asn1Object.FromByteArray( + ReadBytes(endMarker))); + + return new RsaKeyParameters( + false, // not private + rsaPubStructure.Modulus, + rsaPubStructure.PublicExponent); + } + + private AsymmetricKeyParameter ReadPublicKey( + string endMarker) + { + return PublicKeyFactory.CreateKey( + ReadBytes(endMarker)); + } + + /** + * Reads in a X509Certificate. + * + * @return the X509Certificate + * @throws IOException if an I/O error occured + */ + private X509Certificate ReadCertificate( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + try + { + return new X509CertificateParser().ReadCertificate(bytes); + } + catch (Exception e) + { + throw new IOException("problem parsing cert: " + e.ToString()); + } + } + + /** + * Reads in a X509CRL. + * + * @return the X509Certificate + * @throws IOException if an I/O error occured + */ + private X509Crl ReadCrl( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + try + { + return new X509CrlParser().ReadCrl(bytes); + } + catch (Exception e) + { + throw new IOException("problem parsing cert: " + e.ToString()); + } + } + + /** + * Reads in a PKCS10 certification request. + * + * @return the certificate request. + * @throws IOException if an I/O error occured + */ + private Pkcs10CertificationRequest ReadCertificateRequest( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + try + { + return new Pkcs10CertificationRequest(bytes); + } + catch (Exception e) + { + throw new IOException("problem parsing cert: " + e.ToString()); + } + } + + /** + * Reads in a X509 Attribute Certificate. + * + * @return the X509 Attribute Certificate + * @throws IOException if an I/O error occured + */ + private IX509AttributeCertificate ReadAttributeCertificate( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + return new X509V2AttributeCertificate(bytes); + } + + /** + * Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS + * API. + * + * @return the X509Certificate + * @throws IOException if an I/O error occured + */ + // TODO Consider returning Asn1.Pkcs.ContentInfo + private Asn1.Cms.ContentInfo ReadPkcs7( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + try + { + return Asn1.Cms.ContentInfo.GetInstance( + Asn1Object.FromByteArray(bytes)); + } + catch (Exception e) + { + throw new IOException("problem parsing PKCS7 object: " + e.ToString()); + } + } + + /** + * Read a Key Pair + */ + private AsymmetricCipherKeyPair ReadKeyPair( + string type, + string endMarker) + { + // + // extract the key + // + IDictionary fields = new Hashtable(); + byte[] keyBytes = ReadBytesAndFields(endMarker, fields); + + string procType = (string) fields["Proc-Type"]; + + if (procType == "4,ENCRYPTED") + { + if (pFinder == null) + throw new InvalidOperationException("No password finder specified, but a password is required"); + + char[] password = pFinder.GetPassword(); + + if (password == null) + throw new IOException("Password is null, but a password is required"); + + string dekInfo = (string) fields["DEK-Info"]; + string[] tknz = dekInfo.Split(','); + + string dekAlgName = tknz[0].Trim(); + byte[] iv = Hex.Decode(tknz[1].Trim()); + + keyBytes = PemUtilities.Crypt(false, keyBytes, password, dekAlgName, iv); + } + + try + { + AsymmetricKeyParameter pubSpec, privSpec; + Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(keyBytes); + + switch (type) + { + case "RSA": + { + RsaPrivateKeyStructure rsa = new RsaPrivateKeyStructure(seq); + + pubSpec = new RsaKeyParameters(false, rsa.Modulus, rsa.PublicExponent); + privSpec = new RsaPrivateCrtKeyParameters( + rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, + rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, + rsa.Coefficient); + + break; + } + + case "DSA": + { + // TODO Create an ASN1 object somewhere for this? + //DerInteger v = (DerInteger)seq[0]; + DerInteger p = (DerInteger)seq[1]; + DerInteger q = (DerInteger)seq[2]; + DerInteger g = (DerInteger)seq[3]; + DerInteger y = (DerInteger)seq[4]; + DerInteger x = (DerInteger)seq[5]; + + DsaParameters parameters = new DsaParameters(p.Value, q.Value, g.Value); + + privSpec = new DsaPrivateKeyParameters(x.Value, parameters); + pubSpec = new DsaPublicKeyParameters(y.Value, parameters); + + break; + } + + default: + throw new ArgumentException("Unknown key type: " + type, "type"); + } + + return new AsymmetricCipherKeyPair(pubSpec, privSpec); + } + catch (Exception e) + { + throw new IOException( + "problem creating " + type + " private key: " + e.ToString()); + } + } + + // TODO Add an equivalent class for ECNamedCurveParameterSpec? + //private ECNamedCurveParameterSpec ReadECParameters( + private X9ECParameters ReadECParameters( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + DerObjectIdentifier oid = (DerObjectIdentifier) Asn1Object.FromByteArray(bytes); + + //return ECNamedCurveTable.getParameterSpec(oid.Id); + return GetCurveParameters(oid.Id); + } + + //private static ECDomainParameters GetCurveParameters( + private static X9ECParameters GetCurveParameters( + string name) + { + // TODO ECGost3410NamedCurves support (returns ECDomainParameters though) + X9ECParameters ecP = X962NamedCurves.GetByName(name); + + if (ecP == null) + { + ecP = SecNamedCurves.GetByName(name); + if (ecP == null) + { + ecP = NistNamedCurves.GetByName(name); + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.GetByName(name); + + if (ecP == null) + throw new Exception("unknown curve name: " + name); + } + } + } + + //return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); + return ecP; + } + + private AsymmetricCipherKeyPair ReadECPrivateKey( + string endMarker) + { + try + { + byte[] bytes = ReadBytes(endMarker); + ECPrivateKeyStructure pKey = new ECPrivateKeyStructure( + (Asn1Sequence) Asn1Object.FromByteArray(bytes)); + AlgorithmIdentifier algId = new AlgorithmIdentifier( + X9ObjectIdentifiers.IdECPublicKey, pKey.GetParameters()); + + PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey.ToAsn1Object()); + SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.GetPublicKey().GetBytes()); + + // TODO Are the keys returned here ECDSA, as Java version forces? + return new AsymmetricCipherKeyPair( + PublicKeyFactory.CreateKey(pubInfo), + PrivateKeyFactory.CreateKey(privInfo)); + } + catch (InvalidCastException e) + { + throw new IOException("wrong ASN.1 object found in stream.", e); + } + catch (Exception e) + { + throw new IOException("problem parsing EC private key.", e); + } + } + } +} diff --git a/src/core/srcbc/openssl/PEMUtilities.cs b/src/core/srcbc/openssl/PEMUtilities.cs new file mode 100644 index 0000000..e9671eb --- /dev/null +++ b/src/core/srcbc/openssl/PEMUtilities.cs @@ -0,0 +1,138 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.OpenSsl +{ + internal sealed class PemUtilities + { + internal static bool ParseDekAlgName( + string dekAlgName, + out string baseAlg, + out string mode) + { + baseAlg = dekAlgName; + mode = "ECB"; + + if (dekAlgName == "DES-EDE" || dekAlgName == "DES-EDE3") + return true; + + int pos = dekAlgName.LastIndexOf('-'); + if (pos < 0) + return false; + + baseAlg = dekAlgName.Substring(0, pos); + mode = dekAlgName.Substring(pos + 1); + + return true; + } + + internal static byte[] Crypt( + bool encrypt, + byte[] bytes, + char[] password, + string dekAlgName, + byte[] iv) + { + string baseAlg, mode; + if (!ParseDekAlgName(dekAlgName, out baseAlg, out mode)) + throw new ArgumentException("Unknown DEK algorithm: " + dekAlgName, "dekAlgName"); + + string padding; + switch (mode) + { + case "CBC": + case "ECB": + padding = "PKCS5Padding"; + break; + case "CFB": + case "OFB": + padding = "NoPadding"; + break; + default: + throw new ArgumentException("Unknown DEK algorithm: " + dekAlgName, "dekAlgName"); + } + + string algorithm; + + byte[] salt = iv; + switch (baseAlg) + { + case "AES-128": + case "AES-192": + case "AES-256": + algorithm = "AES"; + if (salt.Length > 8) + { + salt = new byte[8]; + Array.Copy(iv, 0, salt, 0, salt.Length); + } + break; + case "BF": + algorithm = "BLOWFISH"; + break; + case "DES": + algorithm = "DES"; + break; + case "DES-EDE": + case "DES-EDE3": + algorithm = "DESede"; + break; + case "RC2": + case "RC2-40": + case "RC2-64": + algorithm = "RC2"; + break; + default: + throw new ArgumentException("Unknown DEK algorithm: " + dekAlgName, "dekAlgName"); + } + + string cipherName = algorithm + "/" + mode + "/" + padding; + IBufferedCipher cipher = CipherUtilities.GetCipher(cipherName); + + ICipherParameters cParams = GetCipherParameters(password, baseAlg, salt); + + if (mode != "ECB") + { + cParams = new ParametersWithIV(cParams, iv); + } + + cipher.Init(encrypt, cParams); + + return cipher.DoFinal(bytes); + } + + private static ICipherParameters GetCipherParameters( + char[] password, + string baseAlg, + byte[] salt) + { + string algorithm; + int keyBits; + switch (baseAlg) + { + case "AES-128": keyBits = 128; algorithm = "AES128"; break; + case "AES-192": keyBits = 192; algorithm = "AES192"; break; + case "AES-256": keyBits = 256; algorithm = "AES256"; break; + case "BF": keyBits = 128; algorithm = "BLOWFISH"; break; + case "DES": keyBits = 64; algorithm = "DES"; break; + case "DES-EDE": keyBits = 128; algorithm = "DESEDE"; break; + case "DES-EDE3": keyBits = 192; algorithm = "DESEDE3"; break; + case "RC2": keyBits = 128; algorithm = "RC2"; break; + case "RC2-40": keyBits = 40; algorithm = "RC2"; break; + case "RC2-64": keyBits = 64; algorithm = "RC2"; break; + default: + return null; + } + + OpenSslPbeParametersGenerator pGen = new OpenSslPbeParametersGenerator(); + + pGen.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password), salt); + + return pGen.GenerateDerivedParameters(algorithm, keyBits); + } + } +} diff --git a/src/core/srcbc/openssl/PEMWriter.cs b/src/core/srcbc/openssl/PEMWriter.cs new file mode 100644 index 0000000..e6fa7cb --- /dev/null +++ b/src/core/srcbc/openssl/PEMWriter.cs @@ -0,0 +1,278 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.OpenSsl +{ + ///
+ /// CertificationRequest ::= Sequence {
+ /// certificationRequestInfo CertificationRequestInfo,
+ /// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
+ /// signature BIT STRING
+ /// }
+ ///
+ /// CertificationRequestInfo ::= Sequence {
+ /// version Integer { v1(0) } (v1,...),
+ /// subject Name,
+ /// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
+ /// attributes [0] Attributes{{ CRIAttributes }}
+ /// }
+ ///
+ /// Attributes { ATTRIBUTE:IOSet } ::= Set OF Attr{{ IOSet }}
+ ///
+ /// Attr { ATTRIBUTE:IOSet } ::= Sequence {
+ /// type ATTRIBUTE.&id({IOSet}),
+ /// values Set SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type})
+ /// }
+ ///
+ /// see
+ public class Pkcs10CertificationRequest
+ : CertificationRequest
+ {
+ private static readonly Hashtable algorithms = new Hashtable();
+ private static readonly Hashtable exParams = new Hashtable();
+ private static readonly Hashtable keyAlgorithms = new Hashtable();
+ private static readonly Hashtable oids = new Hashtable();
+ private static readonly ISet noParams = new HashSet();
+
+ static Pkcs10CertificationRequest()
+ {
+ algorithms.Add("MD2WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.2"));
+ algorithms.Add("MD2WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.2"));
+ algorithms.Add("MD5WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.4"));
+ algorithms.Add("MD5WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.4"));
+ algorithms.Add("RSAWITHMD5", new DerObjectIdentifier("1.2.840.113549.1.1.4"));
+ algorithms.Add("SHA1WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.5"));
+ algorithms.Add("SHA1WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.5"));
+ algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption);
+ algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption);
+ algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption);
+ algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption);
+ algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption);
+ algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption);
+ algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption);
+ algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption);
+ algorithms.Add("SHA1WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+ algorithms.Add("SHA224WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+ algorithms.Add("SHA256WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+ algorithms.Add("SHA384WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+ algorithms.Add("SHA512WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+ algorithms.Add("RSAWITHSHA1", new DerObjectIdentifier("1.2.840.113549.1.1.5"));
+ algorithms.Add("RIPEMD160WITHRSAENCRYPTION", new DerObjectIdentifier("1.3.36.3.3.1.2"));
+ algorithms.Add("RIPEMD160WITHRSA", new DerObjectIdentifier("1.3.36.3.3.1.2"));
+ algorithms.Add("SHA1WITHDSA", new DerObjectIdentifier("1.2.840.10040.4.3"));
+ algorithms.Add("DSAWITHSHA1", new DerObjectIdentifier("1.2.840.10040.4.3"));
+ algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224);
+ algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256);
+ algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1);
+ algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224);
+ algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256);
+ algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384);
+ algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512);
+ algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1);
+ algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+ algorithms.Add("GOST3410WITHGOST3411", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+ algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+ algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+ algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+
+ //
+ // reverse mappings
+ //
+ oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
+ oids.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224WITHRSA");
+ oids.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256WITHRSA");
+ oids.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384WITHRSA");
+ oids.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512WITHRSA");
+ oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, "GOST3411WITHGOST3410");
+ oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001, "GOST3411WITHECGOST3410");
+
+ oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
+ oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
+ oids.Add(new DerObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
+ oids.Add(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1WITHECDSA");
+ oids.Add(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224WITHECDSA");
+ oids.Add(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256WITHECDSA");
+ oids.Add(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384WITHECDSA");
+ oids.Add(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512WITHECDSA");
+ oids.Add(OiwObjectIdentifiers.Sha1WithRsa, "SHA1WITHRSA");
+ oids.Add(OiwObjectIdentifiers.DsaWithSha1, "SHA1WITHDSA");
+ oids.Add(NistObjectIdentifiers.DsaWithSha224, "SHA224WITHDSA");
+ oids.Add(NistObjectIdentifiers.DsaWithSha256, "SHA256WITHDSA");
+
+ //
+ // key types
+ //
+ keyAlgorithms.Add(PkcsObjectIdentifiers.RsaEncryption, "RSA");
+ keyAlgorithms.Add(X9ObjectIdentifiers.IdDsa, "DSA");
+
+ //
+ // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
+ // The parameters field SHALL be NULL for RSA based signature algorithms.
+ //
+ noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1);
+ noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224);
+ noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256);
+ noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384);
+ noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512);
+ noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1);
+ noParams.Add(NistObjectIdentifiers.DsaWithSha224);
+ noParams.Add(NistObjectIdentifiers.DsaWithSha256);
+
+ //
+ // RFC 4491
+ //
+ noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+ noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+
+ //
+ // explicit params
+ //
+ AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance);
+ exParams.Add("SHA1WITHRSAANDMGF1", CreatePssParams(sha1AlgId, 20));
+
+ AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha224, DerNull.Instance);
+ exParams.Add("SHA224WITHRSAANDMGF1", CreatePssParams(sha224AlgId, 28));
+
+ AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance);
+ exParams.Add("SHA256WITHRSAANDMGF1", CreatePssParams(sha256AlgId, 32));
+
+ AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance);
+ exParams.Add("SHA384WITHRSAANDMGF1", CreatePssParams(sha384AlgId, 48));
+
+ AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance);
+ exParams.Add("SHA512WITHRSAANDMGF1", CreatePssParams(sha512AlgId, 64));
+ }
+
+ private static RsassaPssParameters CreatePssParams(
+ AlgorithmIdentifier hashAlgId,
+ int saltSize)
+ {
+ return new RsassaPssParameters(
+ hashAlgId,
+ new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, hashAlgId),
+ new DerInteger(saltSize),
+ new DerInteger(1));
+ }
+
+ public Pkcs10CertificationRequest(
+ byte[] encoded)
+ : base((Asn1Sequence) Asn1Object.FromByteArray(encoded))
+ {
+ }
+
+ public Pkcs10CertificationRequest(
+ Asn1Sequence seq)
+ : base(seq)
+ {
+ }
+
+ public Pkcs10CertificationRequest(
+ Stream input)
+ : base((Asn1Sequence) Asn1Object.FromStream(input))
+ {
+ }
+
+ /// + * If a failure code is assciated with the exception it can be retrieved using + * the getFailureCode() method.
+ */ + public class TspValidationException + : TspException + { + private int failureCode; + + public TspValidationException( + string message) + : base(message) + { + this.failureCode = -1; + } + + public TspValidationException( + string message, + int failureCode) + : base(message) + { + this.failureCode = failureCode; + } + + /** + * Return the failure code associated with this exception - if one is set. + * + * @return the failure code if set, -1 otherwise. + */ + public int FailureCode + { + get { return failureCode; } + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampRequest.cs b/src/core/srcbc/tsp/TimeStampRequest.cs new file mode 100644 index 0000000..84cf311 --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampRequest.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Base class for an RFC 3161 Time Stamp Request. + */ + public class TimeStampRequest + : X509ExtensionBase + { + private TimeStampReq req; + + public TimeStampRequest( + TimeStampReq req) + { + this.req = req; + } + + /** + * Create a TimeStampRequest from the past in byte array. + * + * @param req byte array containing the request. + * @throws IOException if the request is malformed. + */ + public TimeStampRequest( + byte[] req) + : this(new Asn1InputStream(req)) + { + } + + /** + * Create a TimeStampRequest from the past in input stream. + * + * @param in input stream containing the request. + * @throws IOException if the request is malformed. + */ + public TimeStampRequest( + Stream input) + : this(new Asn1InputStream(input)) + { + } + + private TimeStampRequest( + Asn1InputStream str) + { + try + { + this.req = TimeStampReq.GetInstance(str.ReadObject()); + } + catch (InvalidCastException e) + { + throw new IOException("malformed request: " + e); + } + catch (ArgumentException e) + { + throw new IOException("malformed request: " + e); + } + } + + public int Version + { + get { return req.Version.Value.IntValue; } + } + + public string MessageImprintAlgOid + { + get { return req.MessageImprint.HashAlgorithm.ObjectID.Id; } + } + + public byte[] GetMessageImprintDigest() + { + return req.MessageImprint.GetHashedMessage(); + } + + public string ReqPolicy + { + get + { + return req.ReqPolicy == null + ? null + : req.ReqPolicy.Id; + } + } + + public BigInteger Nonce + { + get + { + return req.Nonce == null + ? null + : req.Nonce.Value; + } + } + + public bool CertReq + { + get + { + return req.CertReq == null + ? false + : req.CertReq.IsTrue; + } + } + + /** + * Validate the timestamp request, checking the digest to see if it is of an + * accepted type and whether it is of the correct length for the algorithm specified. + * + * @param algorithms a set of string OIDS giving accepted algorithms. + * @param policies if non-null a set of policies we are willing to sign under. + * @param extensions if non-null a set of extensions we are willing to accept. + * @throws TspException if the request is invalid, or processing fails. + */ + public void Validate( + IList algorithms, + IList policies, + IList extensions) + { + if (!algorithms.Contains(this.MessageImprintAlgOid)) + { + throw new TspValidationException("request contains unknown algorithm.", PkiFailureInfo.BadAlg); + } + + if (policies != null && this.ReqPolicy != null && !policies.Contains(this.ReqPolicy)) + { + throw new TspValidationException("request contains unknown policy.", PkiFailureInfo.UnacceptedPolicy); + } + + if (this.Extensions != null && extensions != null) + { + foreach (DerObjectIdentifier oid in this.Extensions.ExtensionOids) + { + if (!extensions.Contains(oid.Id)) + { + throw new TspValidationException("request contains unknown extension.", + PkiFailureInfo.UnacceptedExtension); + } + } + } + + int digestLength = TspUtil.GetDigestLength(this.MessageImprintAlgOid); + + if (digestLength != this.GetMessageImprintDigest().Length) + { + throw new TspValidationException("imprint digest the wrong length.", + PkiFailureInfo.BadDataFormat); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return req.GetEncoded(); + } + + internal X509Extensions Extensions + { + get { return req.Extensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return Extensions; + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampRequestGenerator.cs b/src/core/srcbc/tsp/TimeStampRequestGenerator.cs new file mode 100644 index 0000000..eadceb5 --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampRequestGenerator.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Generator for RFC 3161 Time Stamp Request objects. + */ + public class TimeStampRequestGenerator + { + private DerObjectIdentifier reqPolicy; + + private DerBoolean certReq; + + private Hashtable extensions = new Hashtable(); + private ArrayList extOrdering = new ArrayList(); + + public void SetReqPolicy( + string reqPolicy) + { + this.reqPolicy = new DerObjectIdentifier(reqPolicy); + } + + public void SetCertReq( + bool certReq) + { + this.certReq = DerBoolean.GetInstance(certReq); + } + + /** + * add a given extension field for the standard extensions tag (tag 3) + * @throws IOException + */ + public void AddExtension( + string oid, + bool critical, + Asn1Encodable value) + { + this.AddExtension(oid, critical, value.GetEncoded()); + } + + /** + * add a given extension field for the standard extensions tag + * The value parameter becomes the contents of the octet string associated + * with the extension. + */ + public void AddExtension( + string oid, + bool critical, + byte[] value) + { + DerObjectIdentifier derOid = new DerObjectIdentifier(oid); + extensions[derOid] = new X509Extension(critical, new DerOctetString(value)); + extOrdering.Add(derOid); + } + + public TimeStampRequest Generate( + string digestAlgorithm, + byte[] digest) + { + return this.Generate(digestAlgorithm, digest, null); + } + + public TimeStampRequest Generate( + string digestAlgorithmOid, + byte[] digest, + BigInteger nonce) + { + if (digestAlgorithmOid == null) + { + throw new ArgumentException("No digest algorithm specified"); + } + + DerObjectIdentifier digestAlgOid = new DerObjectIdentifier(digestAlgorithmOid); + + AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOid, DerNull.Instance); + MessageImprint messageImprint = new MessageImprint(algID, digest); + + X509Extensions ext = null; + + if (extOrdering.Count != 0) + { + ext = new X509Extensions(extOrdering, extensions); + } + + DerInteger derNonce = nonce == null + ? null + : new DerInteger(nonce); + + return new TimeStampRequest( + new TimeStampReq(messageImprint, reqPolicy, derNonce, certReq, ext)); + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampResponse.cs b/src/core/srcbc/tsp/TimeStampResponse.cs new file mode 100644 index 0000000..37971ac --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampResponse.cs @@ -0,0 +1,173 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Base class for an RFC 3161 Time Stamp Response object. + */ + public class TimeStampResponse + { + private TimeStampResp resp; + private TimeStampToken timeStampToken; + + public TimeStampResponse( + TimeStampResp resp) + { + this.resp = resp; + + if (resp.TimeStampToken != null) + { + timeStampToken = new TimeStampToken(resp.TimeStampToken); + } + } + + /** + * Create a TimeStampResponse from a byte array containing an ASN.1 encoding. + * + * @param resp the byte array containing the encoded response. + * @throws TspException if the response is malformed. + * @throws IOException if the byte array doesn't represent an ASN.1 encoding. + */ + public TimeStampResponse( + byte[] resp) + : this(readTimeStampResp(new Asn1InputStream(resp))) + { + } + + /** + * Create a TimeStampResponse from an input stream containing an ASN.1 encoding. + * + * @param input the input stream containing the encoded response. + * @throws TspException if the response is malformed. + * @throws IOException if the stream doesn't represent an ASN.1 encoding. + */ + public TimeStampResponse( + Stream input) + : this(readTimeStampResp(new Asn1InputStream(input))) + { + } + + private static TimeStampResp readTimeStampResp( + Asn1InputStream input) + { + try + { + return TimeStampResp.GetInstance(input.ReadObject()); + } + catch (ArgumentException e) + { + throw new TspException("malformed timestamp response: " + e, e); + } + catch (InvalidCastException e) + { + throw new TspException("malformed timestamp response: " + e, e); + } + } + + public int Status + { + get { return resp.Status.Status.IntValue; } + } + + public string GetStatusString() + { + if (resp.Status.StatusString == null) + { + return null; + } + + StringBuilder statusStringBuf = new StringBuilder(); + PkiFreeText text = resp.Status.StatusString; + for (int i = 0; i != text.Count; i++) + { + statusStringBuf.Append(text[i].GetString()); + } + + return statusStringBuf.ToString(); + } + + public PkiFailureInfo GetFailInfo() + { + if (resp.Status.FailInfo == null) + { + return null; + } + + return new PkiFailureInfo(resp.Status.FailInfo); + } + + public TimeStampToken TimeStampToken + { + get { return timeStampToken; } + } + + /** + * Check this response against to see if it a well formed response for + * the passed in request. Validation will include checking the time stamp + * token if the response status is GRANTED or GRANTED_WITH_MODS. + * + * @param request the request to be checked against + * @throws TspException if the request can not match this response. + */ + public void Validate( + TimeStampRequest request) + { + TimeStampToken tok = this.TimeStampToken; + + if (tok != null) + { + TimeStampTokenInfo tstInfo = tok.TimeStampInfo; + + if (request.Nonce != null && !request.Nonce.Equals(tstInfo.Nonce)) + { + throw new TspValidationException("response contains wrong nonce value."); + } + + if (this.Status != (int) PkiStatus.Granted && this.Status != (int) PkiStatus.GrantedWithMods) + { + throw new TspValidationException("time stamp token found in failed request."); + } + + if (!Arrays.AreEqual(request.GetMessageImprintDigest(), tstInfo.GetMessageImprintDigest())) + { + throw new TspValidationException("response for different message imprint digest."); + } + + if (!tstInfo.MessageImprintAlgOid.Equals(request.MessageImprintAlgOid)) + { + throw new TspValidationException("response for different message imprint algorithm."); + } + + if (tok.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificate] == null) + { + throw new TspValidationException("no signing certificate attribute present."); + } + + if (request.ReqPolicy != null && !request.ReqPolicy.Equals(tstInfo.Policy)) + { + throw new TspValidationException("TSA policy wrong for request."); + } + } + else if (this.Status == (int) PkiStatus.Granted || this.Status == (int) PkiStatus.GrantedWithMods) + { + throw new TspValidationException("no time stamp token found and one expected."); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return resp.GetEncoded(); + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampResponseGenerator.cs b/src/core/srcbc/tsp/TimeStampResponseGenerator.cs new file mode 100644 index 0000000..acd13fd --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampResponseGenerator.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Generator for RFC 3161 Time Stamp Responses. + */ + public class TimeStampResponseGenerator + { + private PkiStatus status; + + private Asn1EncodableVector statusStrings; + + private int failInfo; + private TimeStampTokenGenerator tokenGenerator; + private IList acceptedAlgorithms; + private IList acceptedPolicies; + private IList acceptedExtensions; + + public TimeStampResponseGenerator( + TimeStampTokenGenerator tokenGenerator, + IList acceptedAlgorithms) + : this(tokenGenerator, acceptedAlgorithms, null, null) + { + } + + public TimeStampResponseGenerator( + TimeStampTokenGenerator tokenGenerator, + IList acceptedAlgorithms, + IList acceptedPolicy) + : this(tokenGenerator, acceptedAlgorithms, acceptedPolicy, null) + { + } + + public TimeStampResponseGenerator( + TimeStampTokenGenerator tokenGenerator, + IList acceptedAlgorithms, + IList acceptedPolicies, + IList acceptedExtensions) + { + this.tokenGenerator = tokenGenerator; + this.acceptedAlgorithms = acceptedAlgorithms; + this.acceptedPolicies = acceptedPolicies; + this.acceptedExtensions = acceptedExtensions; + + statusStrings = new Asn1EncodableVector(); + } + + private void addStatusString( + string statusString) + { + statusStrings.Add(new DerUtf8String(statusString)); + } + + private void setFailInfoField(int field) + { + failInfo = failInfo | field; + } + + private PkiStatusInfo getPkiStatusInfo() + { + Asn1EncodableVector v = new Asn1EncodableVector( + new DerInteger((int) status)); + + if (statusStrings.Count > 0) + { + v.Add(new PkiFreeText(new DerSequence(statusStrings))); + } + + if (failInfo != 0) + { + v.Add(new FailInfo(failInfo)); + } + + return new PkiStatusInfo(new DerSequence(v)); + } + + public TimeStampResponse Generate( + TimeStampRequest request, + BigInteger serialNumber, + DateTime genTime) + { + TimeStampResp resp; + + try + { + request.Validate(acceptedAlgorithms, acceptedPolicies, acceptedExtensions); + + status = PkiStatus.Granted; + this.addStatusString("Operation Okay"); + + PkiStatusInfo pkiStatusInfo = getPkiStatusInfo(); + + ContentInfo tstTokenContentInfo; + try + { + TimeStampToken token = tokenGenerator.Generate(request, serialNumber, genTime); + byte[] encoded = token.ToCmsSignedData().GetEncoded(); + + tstTokenContentInfo = ContentInfo.GetInstance(Asn1Object.FromByteArray(encoded)); + } + catch (IOException ioEx) + { + throw new TspException( + "Timestamp token received cannot be converted to ContentInfo", ioEx); + } + + resp = new TimeStampResp(pkiStatusInfo, tstTokenContentInfo); + } + catch (TspValidationException e) + { + status = PkiStatus.Rejection; + + this.setFailInfoField(e.FailureCode); + this.addStatusString(e.Message); + + PkiStatusInfo pkiStatusInfo = getPkiStatusInfo(); + + resp = new TimeStampResp(pkiStatusInfo, null); + } + + try + { + return new TimeStampResponse(resp); + } + catch (IOException) + { + throw new TspException("created badly formatted response!"); + } + } + + class FailInfo + : DerBitString + { + internal FailInfo( + int failInfoValue) + : base(GetBytes(failInfoValue), GetPadBits(failInfoValue)) + { + } + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampToken.cs b/src/core/srcbc/tsp/TimeStampToken.cs new file mode 100644 index 0000000..af613e0 --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampToken.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ess; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Tsp +{ + public class TimeStampToken + { + private readonly CmsSignedData tsToken; + private readonly SignerInformation tsaSignerInfo; +// private readonly DateTime genTime; + private readonly TimeStampTokenInfo tstInfo; + private readonly CertID certID; + + public TimeStampToken( + Asn1.Cms.ContentInfo contentInfo) + : this(new CmsSignedData(contentInfo)) + { + } + + public TimeStampToken( + CmsSignedData signedData) + { + this.tsToken = signedData; + + if (!this.tsToken.SignedContentTypeOid.Equals(PkcsObjectIdentifiers.IdCTTstInfo.Id)) + { + throw new TspValidationException("ContentInfo object not for a time stamp."); + } + + ICollection signers = tsToken.GetSignerInfos().GetSigners(); + + if (signers.Count != 1) + { + throw new ArgumentException("Time-stamp token signed by " + + signers.Count + + " signers, but it must contain just the TSA signature."); + } + + + IEnumerator signerEnum = signers.GetEnumerator(); + + signerEnum.MoveNext(); + tsaSignerInfo = (SignerInformation) signerEnum.Current; + + try + { + CmsProcessable content = tsToken.SignedContent; + MemoryStream bOut = new MemoryStream(); + + content.Write(bOut); + + this.tstInfo = new TimeStampTokenInfo( + TstInfo.GetInstance( + Asn1Object.FromByteArray(bOut.ToArray()))); + + Asn1.Cms.Attribute attr = tsaSignerInfo.SignedAttributes[ + PkcsObjectIdentifiers.IdAASigningCertificate]; + +// if (attr == null) +// { +// throw new TspValidationException( +// "no signing certificate attribute found, time stamp invalid."); +// } +// +// SigningCertificate signCert = SigningCertificate.GetInstance( +// attr.AttrValues[0]); +// +// this.certID = EssCertID.GetInstance(signCert.GetCerts()[0]); + + if (attr != null) + { + SigningCertificate signCert = SigningCertificate.GetInstance(attr.AttrValues[0]); + + this.certID = new CertID(EssCertID.GetInstance(signCert.GetCerts()[0])); + } + else + { + attr = tsaSignerInfo.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificateV2]; + + if (attr == null) + throw new TspValidationException("no signing certificate attribute found, time stamp invalid."); + + SigningCertificateV2 signCertV2 = SigningCertificateV2.GetInstance(attr.AttrValues[0]); + + this.certID = new CertID(EssCertIDv2.GetInstance(signCertV2.GetCerts()[0])); + } + } + catch (CmsException e) + { + throw new TspException(e.Message, e.InnerException); + } + } + + public TimeStampTokenInfo TimeStampInfo + { + get { return tstInfo; } + } + + public SignerID SignerID + { + get { return tsaSignerInfo.SignerID; } + } + + public Asn1.Cms.AttributeTable SignedAttributes + { + get { return tsaSignerInfo.SignedAttributes; } + } + + public Asn1.Cms.AttributeTable UnsignedAttributes + { + get { return tsaSignerInfo.UnsignedAttributes; } + } + +// public IX509Store GetCertificatesAndCrls( +// string type) +// { +// return tsToken.GetCertificatesAndCrls(type); +// } + + public IX509Store GetCertificates( + string type) + { + return tsToken.GetCertificates(type); + } + + public IX509Store GetCrls( + string type) + { + return tsToken.GetCrls(type); + } + + /** + * Validate the time stamp token. + *+ * To be valid the token must be signed by the passed in certificate and + * the certificate must be the one referred to by the SigningCertificate + * attribute included in the hashed attributes of the token. The + * certificate must also have the ExtendedKeyUsageExtension with only + * KeyPurposeID.IdKPTimeStamping and have been valid at the time the + * timestamp was created. + *
+ *+ * A successful call to validate means all the above are true. + *
+ */ + public void Validate( + X509Certificate cert) + { + IDigest digest; + try + { + digest = DigestUtilities.GetDigest(certID.GetHashAlgorithm()); + } + catch (SecurityUtilityException e) + { + throw new TspException("cannot find algorithm: " + e.Message, e); + } + + try + { + byte[] certEncoded = cert.GetEncoded(); + digest.BlockUpdate(certEncoded, 0, certEncoded.Length); + byte[] hash = DigestUtilities.DoFinal(digest); + + if (!Arrays.AreEqual(certID.GetCertHash(), hash)) + { + throw new TspValidationException("certificate hash does not match certID hash."); + } + + if (certID.IssuerSerial != null) + { + if (!certID.IssuerSerial.Serial.Value.Equals(cert.SerialNumber)) + { + throw new TspValidationException("certificate serial number does not match certID for signature."); + } + + GeneralName[] names = certID.IssuerSerial.Issuer.GetNames(); + X509Name principal = PrincipalUtilities.GetIssuerX509Principal(cert); + bool found = false; + + for (int i = 0; i != names.Length; i++) + { + if (names[i].TagNo == 4 + && X509Name.GetInstance(names[i].Name).Equivalent(principal)) + { + found = true; + break; + } + } + + if (!found) + { + throw new TspValidationException("certificate name does not match certID for signature. "); + } + } + + TspUtil.ValidateCertificate(cert); + + cert.CheckValidity(tstInfo.GenTime); + + if (!tsaSignerInfo.Verify(cert)) + { + throw new TspValidationException("signature not created by certificate."); + } + } + catch (CmsException e) + { + if (e.InnerException != null) + { + throw new TspException(e.Message, e.InnerException); + } + + throw new TspException("CMS exception: " + e, e); + } + catch (CertificateEncodingException e) + { + throw new TspException("problem processing certificate: " + e, e); + } + } + + /** + * Return the underlying CmsSignedData object. + * + * @return the underlying CMS structure. + */ + public CmsSignedData ToCmsSignedData() + { + return tsToken; + } + + /** + * Return a ASN.1 encoded byte stream representing the encoded object. + * + * @throws IOException if encoding fails. + */ + public byte[] GetEncoded() + { + return tsToken.GetEncoded(); + } + + + // perhaps this should be done using an interface on the ASN.1 classes... + private class CertID + { + private EssCertID certID; + private EssCertIDv2 certIDv2; + + internal CertID(EssCertID certID) + { + this.certID = certID; + this.certIDv2 = null; + } + + internal CertID(EssCertIDv2 certID) + { + this.certIDv2 = certID; + this.certID = null; + } + + public string GetHashAlgorithm() + { + if (certID != null) + return "SHA-1"; + + if (NistObjectIdentifiers.IdSha256.Equals(certIDv2.HashAlgorithm.ObjectID)) + return "SHA-256"; + + return certIDv2.HashAlgorithm.ObjectID.Id; + } + + public byte[] GetCertHash() + { + return certID != null + ? certID.GetCertHash() + : certIDv2.GetCertHash(); + } + + public IssuerSerial IssuerSerial + { + get + { + return certID != null + ? certID.IssuerSerial + : certIDv2.IssuerSerial; + } + } + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampTokenGenerator.cs b/src/core/srcbc/tsp/TimeStampTokenGenerator.cs new file mode 100644 index 0000000..a2a8ef9 --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampTokenGenerator.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ess; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Tsp +{ + public class TimeStampTokenGenerator + { + private int accuracySeconds = -1; + private int accuracyMillis = -1; + private int accuracyMicros = -1; + private bool ordering = false; + private GeneralName tsa = null; + private string tsaPolicyOID; + + private AsymmetricKeyParameter key; + private X509Certificate cert; + private string digestOID; + private Asn1.Cms.AttributeTable signedAttr; + private Asn1.Cms.AttributeTable unsignedAttr; + private IX509Store x509Certs; + private IX509Store x509Crls; + + /** + * basic creation - only the default attributes will be included here. + */ + public TimeStampTokenGenerator( + AsymmetricKeyParameter key, + X509Certificate cert, + string digestOID, + string tsaPolicyOID) + : this(key, cert, digestOID, tsaPolicyOID, null, null) + { + } + + /** + * create with a signer with extra signed/unsigned attributes. + */ + public TimeStampTokenGenerator( + AsymmetricKeyParameter key, + X509Certificate cert, + string digestOID, + string tsaPolicyOID, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + this.key = key; + this.cert = cert; + this.digestOID = digestOID; + this.tsaPolicyOID = tsaPolicyOID; + this.unsignedAttr = unsignedAttr; + + TspUtil.ValidateCertificate(cert); + + // + // add the essCertid + // + Hashtable signedAttrs; + if (signedAttr != null) + { + signedAttrs = signedAttr.ToHashtable(); + } + else + { + signedAttrs = new Hashtable(); + } + + IDigest digest; + try + { + digest = DigestUtilities.GetDigest("SHA-1"); + } + catch (Exception e) + { + throw new TspException("Can't find a SHA-1 implementation.", e); + } + + try + { + byte[] certEncoded = cert.GetEncoded(); + digest.BlockUpdate(certEncoded, 0, certEncoded.Length); + byte[] hash = DigestUtilities.DoFinal(digest); + + EssCertID essCertid = new EssCertID(hash); + + Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute( + PkcsObjectIdentifiers.IdAASigningCertificate, + new DerSet(new SigningCertificate(essCertid))); + + signedAttrs[attr.AttrType] = attr; + } + catch (CertificateEncodingException e) + { + throw new TspException("Exception processing certificate.", e); + } + + this.signedAttr = new Asn1.Cms.AttributeTable(signedAttrs); + } + + public void SetCertificates( + IX509Store certificates) + { + this.x509Certs = certificates; + } + + public void SetCrls( + IX509Store crls) + { + this.x509Crls = crls; + } + + public void SetAccuracySeconds( + int accuracySeconds) + { + this.accuracySeconds = accuracySeconds; + } + + public void SetAccuracyMillis( + int accuracyMillis) + { + this.accuracyMillis = accuracyMillis; + } + + public void SetAccuracyMicros( + int accuracyMicros) + { + this.accuracyMicros = accuracyMicros; + } + + public void SetOrdering( + bool ordering) + { + this.ordering = ordering; + } + + public void SetTsa( + GeneralName tsa) + { + this.tsa = tsa; + } + + //------------------------------------------------------------------------------ + + public TimeStampToken Generate( + TimeStampRequest request, + BigInteger serialNumber, + DateTime genTime) + { + DerObjectIdentifier digestAlgOID = new DerObjectIdentifier(request.MessageImprintAlgOid); + + AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOID, DerNull.Instance); + MessageImprint messageImprint = new MessageImprint(algID, request.GetMessageImprintDigest()); + + Accuracy accuracy = null; + if (accuracySeconds > 0 || accuracyMillis > 0 || accuracyMicros > 0) + { + DerInteger seconds = null; + if (accuracySeconds > 0) + { + seconds = new DerInteger(accuracySeconds); + } + + DerInteger millis = null; + if (accuracyMillis > 0) + { + millis = new DerInteger(accuracyMillis); + } + + DerInteger micros = null; + if (accuracyMicros > 0) + { + micros = new DerInteger(accuracyMicros); + } + + accuracy = new Accuracy(seconds, millis, micros); + } + + DerBoolean derOrdering = null; + if (ordering) + { + derOrdering = DerBoolean.GetInstance(ordering); + } + + DerInteger nonce = null; + if (request.Nonce != null) + { + nonce = new DerInteger(request.Nonce); + } + + DerObjectIdentifier tsaPolicy = new DerObjectIdentifier(tsaPolicyOID); + if (request.ReqPolicy != null) + { + tsaPolicy = new DerObjectIdentifier(request.ReqPolicy); + } + + TstInfo tstInfo = new TstInfo(tsaPolicy, messageImprint, + new DerInteger(serialNumber), new DerGeneralizedTime(genTime), accuracy, + derOrdering, nonce, tsa, request.Extensions); + + try + { + CmsSignedDataGenerator signedDataGenerator = new CmsSignedDataGenerator(); + + byte[] derEncodedTstInfo = tstInfo.GetDerEncoded(); + + if (request.CertReq) + { + signedDataGenerator.AddCertificates(x509Certs); + } + + signedDataGenerator.AddCrls(x509Crls); + signedDataGenerator.AddSigner(key, cert, digestOID, signedAttr, unsignedAttr); + + CmsSignedData signedData = signedDataGenerator.Generate( + PkcsObjectIdentifiers.IdCTTstInfo.Id, + new CmsProcessableByteArray(derEncodedTstInfo), + true); + + return new TimeStampToken(signedData); + } + catch (CmsException cmsEx) + { + throw new TspException("Error generating time-stamp token", cmsEx); + } + catch (IOException e) + { + throw new TspException("Exception encoding info", e); + } + catch (X509StoreException e) + { + throw new TspException("Exception handling CertStore", e); + } +// catch (InvalidAlgorithmParameterException e) +// { +// throw new TspException("Exception handling CertStore CRLs", e); +// } + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampTokenInfo.cs b/src/core/srcbc/tsp/TimeStampTokenInfo.cs new file mode 100644 index 0000000..a2391ca --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampTokenInfo.cs @@ -0,0 +1,102 @@ +using System; + +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Tsp +{ + public class TimeStampTokenInfo + { + private TstInfo tstInfo; + private DateTime genTime; + + public TimeStampTokenInfo( + TstInfo tstInfo) + { + this.tstInfo = tstInfo; + + try + { + this.genTime = tstInfo.GenTime.ToDateTime(); + } + catch (Exception e) + { + throw new TspException("unable to parse genTime field: " + e.Message); + } + } + + public bool IsOrdered + { + get { return tstInfo.Ordering.IsTrue; } + } + + public Accuracy Accuracy + { + get { return tstInfo.Accuracy; } + } + + public DateTime GenTime + { + get { return genTime; } + } + + public GenTimeAccuracy GenTimeAccuracy + { + get + { + return this.Accuracy == null + ? null + : new GenTimeAccuracy(this.Accuracy); + } + } + + public string Policy + { + get { return tstInfo.Policy.Id; } + } + + public BigInteger SerialNumber + { + get { return tstInfo.SerialNumber.Value; } + } + + public GeneralName Tsa + { + get { return tstInfo.Tsa; } + } + + /** + * @return the nonce value, null if there isn't one. + */ + public BigInteger Nonce + { + get + { + return tstInfo.Nonce == null + ? null + : tstInfo.Nonce.Value; + } + } + + public string MessageImprintAlgOid + { + get { return tstInfo.MessageImprint.HashAlgorithm.ObjectID.Id; } + } + + public byte[] GetMessageImprintDigest() + { + return tstInfo.MessageImprint.GetHashedMessage(); + } + + public byte[] GetEncoded() + { + return tstInfo.GetEncoded(); + } + + public TstInfo TstInfo + { + get { return tstInfo; } + } + } +} diff --git a/src/core/srcbc/util/Arrays.cs b/src/core/srcbc/util/Arrays.cs new file mode 100644 index 0000000..af16246 --- /dev/null +++ b/src/core/srcbc/util/Arrays.cs @@ -0,0 +1,134 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Utilities +{ + + ///+ * The purpose of UrlBase64 encoding is to provide a compact encoding of binary + * data that is safe for use as an URL parameter. Base64 encoding does not + * produce encoded values that are safe for use in URLs, since "/" can be + * interpreted as a path delimiter; "+" is the encoded form of a space; and + * "=" is used to separate a name from the corresponding value in an URL + * parameter. + *
+ */ + public class UrlBase64 + { + private static readonly IEncoder encoder = new UrlBase64Encoder(); + + /** + * Encode the input data producing a URL safe base 64 encoded byte array. + * + * @return a byte array containing the URL safe base 64 encoded data. + */ + public static byte[] Encode( + byte[] data) + { + MemoryStream bOut = new MemoryStream(); + + try + { + encoder.Encode(data, 0, data.Length, bOut); + } + catch (IOException e) + { + throw new Exception("exception encoding URL safe base64 string: " + e.Message, e); + } + + return bOut.ToArray(); + } + + /** + * Encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int Encode( + byte[] data, + Stream outStr) + { + return encoder.Encode(data, 0, data.Length, outStr); + } + + /** + * Decode the URL safe base 64 encoded input data - white space will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] Decode( + byte[] data) + { + MemoryStream bOut = new MemoryStream(); + + try + { + encoder.Decode(data, 0, data.Length, bOut); + } + catch (IOException e) + { + throw new Exception("exception decoding URL safe base64 string: " + e.Message, e); + } + + return bOut.ToArray(); + } + + /** + * decode the URL safe base 64 encoded byte data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public static int Decode( + byte[] data, + Stream outStr) + { + return encoder.Decode(data, 0, data.Length, outStr); + } + + /** + * decode the URL safe base 64 encoded string data - whitespace will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] Decode( + string data) + { + MemoryStream bOut = new MemoryStream(); + + try + { + encoder.DecodeString(data, bOut); + } + catch (IOException e) + { + throw new Exception("exception decoding URL safe base64 string: " + e.Message, e); + } + + return bOut.ToArray(); + } + + /** + * Decode the URL safe base 64 encoded string data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public static int Decode( + string data, + Stream outStr) + { + return encoder.DecodeString(data, outStr); + } + } +} diff --git a/src/core/srcbc/util/encoders/UrlBase64Encoder.cs b/src/core/srcbc/util/encoders/UrlBase64Encoder.cs new file mode 100644 index 0000000..99c9c9c --- /dev/null +++ b/src/core/srcbc/util/encoders/UrlBase64Encoder.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /** + * Convert binary data to and from UrlBase64 encoding. This is identical to + * Base64 encoding, except that the padding character is "." and the other + * non-alphanumeric characters are "-" and "_" instead of "+" and "/". + *+ * The purpose of UrlBase64 encoding is to provide a compact encoding of binary + * data that is safe for use as an URL parameter. Base64 encoding does not + * produce encoded values that are safe for use in URLs, since "/" can be + * interpreted as a path delimiter; "+" is the encoded form of a space; and + * "=" is used to separate a name from the corresponding value in an URL + * parameter. + *
+ */ + public class UrlBase64Encoder + : Base64Encoder + { + public UrlBase64Encoder() + { + encodingTable[encodingTable.Length - 2] = (byte) '-'; + encodingTable[encodingTable.Length - 1] = (byte) '_'; + padding = (byte) '.'; + // we must re-create the decoding table with the new encoded values. + InitialiseDecodingTable(); + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/io/BaseInputStream.cs b/src/core/srcbc/util/io/BaseInputStream.cs new file mode 100644 index 0000000..4c28e13 --- /dev/null +++ b/src/core/srcbc/util/io/BaseInputStream.cs @@ -0,0 +1,47 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public abstract class BaseInputStream : Stream + { + private bool closed; + + public sealed override bool CanRead { get { return !closed; } } + public sealed override bool CanSeek { get { return false; } } + public sealed override bool CanWrite { get { return false; } } + public override void Close() { closed = true; } + public sealed override void Flush() {} + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + int pos = offset; + try + { + int end = offset + count; + while (pos < end) + { + int b = ReadByte(); + if (b == -1) break; + buffer[pos++] = (byte) b; + } + } + catch (IOException) + { + if (pos == offset) throw; + } + return pos - offset; + } + + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + } +} diff --git a/src/core/srcbc/util/io/BaseOutputStream.cs b/src/core/srcbc/util/io/BaseOutputStream.cs new file mode 100644 index 0000000..339eef4 --- /dev/null +++ b/src/core/srcbc/util/io/BaseOutputStream.cs @@ -0,0 +1,47 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public abstract class BaseOutputStream : Stream + { + private bool closed; + + public sealed override bool CanRead { get { return false; } } + public sealed override bool CanSeek { get { return false; } } + public sealed override bool CanWrite { get { return !closed; } } + public override void Close() { closed = true; } + public override void Flush() {} + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + + 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); + + for (int i = offset; i < end; ++i) + { + this.WriteByte(buffer[i]); + } + } + + public virtual void Write(params byte[] buffer) + { + Write(buffer, 0, buffer.Length); + } + } +} diff --git a/src/core/srcbc/util/io/PushbackStream.cs b/src/core/srcbc/util/io/PushbackStream.cs new file mode 100644 index 0000000..ce34e79 --- /dev/null +++ b/src/core/srcbc/util/io/PushbackStream.cs @@ -0,0 +1,52 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1.Utilities; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class PushbackStream + : FilterStream + { + private int buf = -1; + + public PushbackStream( + Stream s) + : base(s) + { + } + + public override int ReadByte() + { + if (buf != -1) + { + int tmp = buf; + buf = -1; + return tmp; + } + + return base.ReadByte(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (buf != -1 && count > 0) + { + // TODO Can this case be made more efficient? + buffer[offset] = (byte) buf; + buf = -1; + return 1; + } + + return base.Read(buffer, offset, count); + } + + public virtual void Unread(int b) + { + if (buf != -1) + throw new InvalidOperationException("Can only push back one byte"); + + buf = b & 0xFF; + } + } +} diff --git a/src/core/srcbc/util/io/Streams.cs b/src/core/srcbc/util/io/Streams.cs new file mode 100644 index 0000000..38f2633 --- /dev/null +++ b/src/core/srcbc/util/io/Streams.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public sealed class Streams + { + private const int BufferSize = 512; + + private Streams() + { + } + + public static void Drain(Stream inStr) + { + byte[] bs = new byte[BufferSize]; + while (inStr.Read(bs, 0, bs.Length) > 0) + { + } + } + + public static byte[] ReadAll(Stream inStr) + { + MemoryStream buf = new MemoryStream(); + PipeAll(inStr, buf); + return buf.ToArray(); + } + + public static int ReadFully(Stream inStr, byte[] buf) + { + return ReadFully(inStr, buf, 0, buf.Length); + } + + public static int ReadFully(Stream inStr, byte[] buf, int off, int len) + { + int totalRead = 0; + while (totalRead < len) + { + int numRead = inStr.Read(buf, off + totalRead, len - totalRead); + if (numRead < 1) + break; + totalRead += numRead; + } + return totalRead; + } + + public static void PipeAll(Stream inStr, Stream outStr) + { + byte[] bs = new byte[BufferSize]; + int numRead; + while ((numRead = inStr.Read(bs, 0, bs.Length)) > 0) + { + outStr.Write(bs, 0, numRead); + } + } + } +} diff --git a/src/core/srcbc/util/net/IPAddress.cs b/src/core/srcbc/util/net/IPAddress.cs new file mode 100644 index 0000000..08976f6 --- /dev/null +++ b/src/core/srcbc/util/net/IPAddress.cs @@ -0,0 +1,112 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Utilities.Net +{ + public class IPAddress + { + /** + * Validate the given IPv4 or IPv6 address. + * + * @param address the IP address as a string. + * + * @return true if a valid address, false otherwise + */ + public static bool IsValid( + string address) + { + return IsValidIPv4(address) || IsValidIPv6(address); + } + + /** + * Validate the given IPv4 address. + * + * @param address the IP address as a string. + * + * @return true if a valid IPv4 address, false otherwise + */ + private static bool IsValidIPv4( + string address) + { + if (address.Length == 0) + return false; + + BigInteger octet; + int octets = 0; + + string temp = address + "."; + + int pos; + int start = 0; + while (start < temp.Length + && (pos = temp.IndexOf('.', start)) > start) + { + if (octets == 4) + return false; + + try + { + octet = new BigInteger(temp.Substring(start, pos - start)); + } + catch (FormatException) + { + return false; + } + + if (octet.SignValue < 0 || octet.BitLength > 8) + return false; + + start = pos + 1; + ++octets; + } + + return octets == 4; + } + + /** + * Validate the given IPv6 address. + * + * @param address the IP address as a string. + * + * @return true if a valid IPv4 address, false otherwise + */ + private static bool IsValidIPv6( + string address) + { + if (address.Length == 0) + return false; + + BigInteger octet; + int octets = 0; + + string temp = address + ":"; + + int pos; + int start = 0; + while (start < temp.Length + && (pos = temp.IndexOf(':', start)) > start) + { + if (octets == 8) + return false; + + try + { + octet = new BigInteger(temp.Substring(start, pos - start), 16); + } + catch (FormatException) + { + return false; + } + + if (octet.SignValue < 0 || octet.BitLength > 16) + return false; + + start = pos + 1; + octets++; + } + + return octets == 8; + } + } +} diff --git a/src/core/srcbc/util/zlib/Adler32.cs b/src/core/srcbc/util/zlib/Adler32.cs new file mode 100644 index 0000000..4926360 --- /dev/null +++ b/src/core/srcbc/util/zlib/Adler32.cs @@ -0,0 +1,88 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1.1.1 2007/01/24 16:41:26 psoares33 Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class Adler32{ + + // largest prime smaller than 65536 + private const int BASE=65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX=5552; + + internal long adler32(long adler, byte[] buf, int index, int len){ + if(buf == null){ return 1L; } + + long s1=adler&0xffff; + long s2=(adler>>16)&0xffff; + int k; + + while(len > 0) { + k=len+ /// Holder ::= SEQUENCE { + /// baseCertificateID [0] IssuerSerial OPTIONAL, + /// -- the issuer and serial number of + /// -- the holder's Public Key Certificate + /// entityName [1] GeneralNames OPTIONAL, + /// -- the name of the claimant or role + /// objectDigestInfo [2] ObjectDigestInfo OPTIONAL + /// -- used to directly authenticate the holder, + /// -- for example, an executable + /// } + ///+ ///
+ * digestedObjectType
can be one of the following:
+ *
otherObjectTypeID
must not be empty.This cannot be used if a v1 attribute certificate is used.
+ * + * @param digestedObjectType The digest object type. + * @param digestAlgorithm The algorithm identifier for the hash. + * @param otherObjectTypeID The object type ID if + *digestedObjectType
is
+ * otherObjectDigest
.
+ * @param objectDigest The hash value.
+ */
+ public AttributeCertificateHolder(
+ int digestedObjectType,
+ string digestAlgorithm,
+ string otherObjectTypeID,
+ byte[] objectDigest)
+ {
+ // TODO Allow 'objectDigest' to be null?
+
+ holder = new Holder(new ObjectDigestInfo(digestedObjectType, otherObjectTypeID,
+ new AlgorithmIdentifier(digestAlgorithm), Arrays.Clone(objectDigest)));
+ }
+
+ /**
+ * Returns the digest object type if an object digest info is used.
+ * + *
otherObjectTypeID
must not be empty.null
if no object
+ * digest info is set.
+ */
+ public string DigestAlgorithm
+ {
+ get
+ {
+ ObjectDigestInfo odi = holder.ObjectDigestInfo;
+
+ return odi == null
+ ? null
+ : odi.DigestAlgorithm.ObjectID.Id;
+ }
+ }
+
+ /**
+ * Returns the hash if an object digest info is used.
+ *
+ * @return The hash or null
if no object digest info is set.
+ */
+ public byte[] GetObjectDigest()
+ {
+ ObjectDigestInfo odi = holder.ObjectDigestInfo;
+
+ return odi == null
+ ? null
+ : odi.ObjectDigest.GetBytes();
+ }
+
+ /**
+ * Returns the digest algorithm ID if an object digest info is used.
+ *
+ * @return The digest algorithm ID or null
if no object
+ * digest info is set.
+ */
+ public string OtherObjectTypeID
+ {
+ get
+ {
+ ObjectDigestInfo odi = holder.ObjectDigestInfo;
+
+ return odi == null
+ ? null
+ : odi.OtherObjectTypeID.Id;
+ }
+ }
+
+ private GeneralNames GenerateGeneralNames(
+ X509Name principal)
+ {
+// return GeneralNames.GetInstance(new DerSequence(new GeneralName(principal)));
+ return new GeneralNames(new GeneralName(principal));
+ }
+
+ private bool MatchesDN(
+ X509Name subject,
+ GeneralNames targets)
+ {
+ GeneralName[] names = targets.GetNames();
+
+ for (int i = 0; i != names.Length; i++)
+ {
+ GeneralName gn = names[i];
+
+ if (gn.TagNo == GeneralName.DirectoryName)
+ {
+ try
+ {
+ if (X509Name.GetInstance(gn.Name).Equivalent(subject))
+ {
+ return true;
+ }
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private object[] GetNames(
+ GeneralName[] names)
+ {
+ ArrayList l = new ArrayList(names.Length);
+
+ for (int i = 0; i != names.Length; i++)
+ {
+ if (names[i].TagNo == GeneralName.DirectoryName)
+ {
+ l.Add(X509Name.GetInstance(names[i].Name));
+ }
+ }
+
+ return l.ToArray();
+ }
+
+ private X509Name[] GetPrincipals(
+ GeneralNames names)
+ {
+ object[] p = this.GetNames(names.GetNames());
+ ArrayList l = new ArrayList(p.Length);
+
+ for (int i = 0; i != p.Length; i++)
+ {
+ if (p[i] is X509Name)
+ {
+ l.Add(p[i]);
+ }
+ }
+
+ return (X509Name[]) l.ToArray(typeof(X509Name));
+ }
+
+ /**
+ * Return any principal objects inside the attribute certificate holder entity names field.
+ *
+ * @return an array of IPrincipal objects (usually X509Name), null if no entity names field is set.
+ */
+ public X509Name[] GetEntityNames()
+ {
+ if (holder.EntityName != null)
+ {
+ return GetPrincipals(holder.EntityName);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the principals associated with the issuer attached to this holder
+ *
+ * @return an array of principals, null if no BaseCertificateID is set.
+ */
+ public X509Name[] GetIssuer()
+ {
+ if (holder.BaseCertificateID != null)
+ {
+ return GetPrincipals(holder.BaseCertificateID.Issuer);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the serial number associated with the issuer attached to this holder.
+ *
+ * @return the certificate serial number, null if no BaseCertificateID is set.
+ */
+ public BigInteger SerialNumber
+ {
+ get
+ {
+ if (holder.BaseCertificateID != null)
+ {
+ return holder.BaseCertificateID.Serial.Value;
+ }
+
+ return null;
+ }
+ }
+
+ public object Clone()
+ {
+ return new AttributeCertificateHolder((Asn1Sequence)holder.ToAsn1Object());
+ }
+
+ public bool Match(
+// Certificate cert)
+ X509Certificate x509Cert)
+ {
+// if (!(cert is X509Certificate))
+// {
+// return false;
+// }
+//
+// X509Certificate x509Cert = (X509Certificate)cert;
+
+ try
+ {
+ if (holder.BaseCertificateID != null)
+ {
+ return holder.BaseCertificateID.Serial.Value.Equals(x509Cert.SerialNumber)
+ && MatchesDN(PrincipalUtilities.GetIssuerX509Principal(x509Cert), holder.BaseCertificateID.Issuer);
+ }
+
+ if (holder.EntityName != null)
+ {
+ if (MatchesDN(PrincipalUtilities.GetSubjectX509Principal(x509Cert), holder.EntityName))
+ {
+ return true;
+ }
+ }
+
+ if (holder.ObjectDigestInfo != null)
+ {
+ IDigest md = null;
+ try
+ {
+ md = DigestUtilities.GetDigest(DigestAlgorithm);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ switch (DigestedObjectType)
+ {
+ case ObjectDigestInfo.PublicKey:
+ {
+ // TODO: DSA Dss-parms
+
+ //byte[] b = x509Cert.GetPublicKey().getEncoded();
+ // TODO Is this the right way to encode?
+ byte[] b = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(
+ x509Cert.GetPublicKey()).GetEncoded();
+ md.BlockUpdate(b, 0, b.Length);
+ break;
+ }
+
+ case ObjectDigestInfo.PublicKeyCert:
+ {
+ byte[] b = x509Cert.GetEncoded();
+ md.BlockUpdate(b, 0, b.Length);
+ break;
+ }
+
+ // TODO Default handler?
+ }
+
+ // TODO Shouldn't this be the other way around?
+ if (!Arrays.AreEqual(DigestUtilities.DoFinal(md), GetObjectDigest()))
+ {
+ return false;
+ }
+ }
+ }
+ catch (CertificateEncodingException)
+ {
+ return false;
+ }
+
+ return false;
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj is AttributeCertificateHolder))
+ {
+ return false;
+ }
+
+ AttributeCertificateHolder other = (AttributeCertificateHolder)obj;
+
+ return this.holder.Equals(other.holder);
+ }
+
+ public override int GetHashCode()
+ {
+ return this.holder.GetHashCode();
+ }
+
+ public bool Match(
+ object obj)
+ {
+ if (!(obj is X509Certificate))
+ {
+ return false;
+ }
+
+// return Match((Certificate)obj);
+ return Match((X509Certificate)obj);
+ }
+ }
+}
diff --git a/src/core/srcbc/x509/AttributeCertificateIssuer.cs b/src/core/srcbc/x509/AttributeCertificateIssuer.cs
new file mode 100644
index 0000000..4c0a076
--- /dev/null
+++ b/src/core/srcbc/x509/AttributeCertificateIssuer.cs
@@ -0,0 +1,177 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.X509
+{
+ /**
+ * Carrying class for an attribute certificate issuer.
+ */
+ public class AttributeCertificateIssuer
+ //: CertSelector, Selector
+ : IX509Selector
+ {
+ internal readonly Asn1Encodable form;
+
+ /**
+ * Set the issuer directly with the ASN.1 structure.
+ *
+ * @param issuer The issuer
+ */
+ internal AttributeCertificateIssuer(
+ AttCertIssuer issuer)
+ {
+ form = issuer.Issuer;
+ }
+
+ public AttributeCertificateIssuer(
+ X509Name principal)
+ {
+// form = new V2Form(GeneralNames.GetInstance(new DerSequence(new GeneralName(principal))));
+ form = new V2Form(new GeneralNames(new GeneralName(principal)));
+ }
+
+ private object[] GetNames()
+ {
+ GeneralNames name;
+ if (form is V2Form)
+ {
+ name = ((V2Form)form).IssuerName;
+ }
+ else
+ {
+ name = (GeneralNames)form;
+ }
+
+ GeneralName[] names = name.GetNames();
+
+ ArrayList l = new ArrayList(names.Length);
+
+ for (int i = 0; i != names.Length; i++)
+ {
+ if (names[i].TagNo == GeneralName.DirectoryName)
+ {
+ l.Add(X509Name.GetInstance(names[i].Name));
+ }
+ }
+
+ return l.ToArray();
+ }
+
+ /// + /// Use this in preference to trying to recreate a principal from a string, not all + /// DNs are what they should be, so it's best to leave them encoded where they + /// can be.
+ ///+ * At the moment this will deal with "-----BEGIN CERTIFICATE-----" to "-----END CERTIFICATE-----" + * base 64 encoded certs, as well as the BER binaries of certificates and some classes of PKCS#7 + * objects.
+ */ + public class X509CertificateParser + { + private static readonly PemParser PemCertParser = new PemParser("CERTIFICATE"); + + private Asn1Set sData; + private int sDataObjectCount; + private Stream currentStream; + + private X509Certificate ReadDerCertificate( + Asn1InputStream dIn) + { + Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject(); + + if (seq.Count > 1 && seq[0] is DerObjectIdentifier) + { + if (seq[0].Equals(PkcsObjectIdentifiers.SignedData)) + { + sData = SignedData.GetInstance( + Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Certificates; + + return GetCertificate(); + } + } + + return CreateX509Certificate(X509CertificateStructure.GetInstance(seq)); + } + + private X509Certificate GetCertificate() + { + if (sData != null) + { + while (sDataObjectCount < sData.Count) + { + object obj = sData[sDataObjectCount++]; + + if (obj is Asn1Sequence) + { + return CreateX509Certificate( + X509CertificateStructure.GetInstance(obj)); + } + } + } + + return null; + } + + private X509Certificate ReadPemCertificate( + Stream inStream) + { + Asn1Sequence seq = PemCertParser.ReadPemObject(inStream); + + return seq == null + ? null + : CreateX509Certificate(X509CertificateStructure.GetInstance(seq)); + } + + protected virtual X509Certificate CreateX509Certificate( + X509CertificateStructure c) + { + return new X509Certificate(c); + } + + ///isIndirect
+ * is false
{@link #getCertificateIssuer()} will always
+ * return null
, previousCertificateIssuer
is
+ * ignored. If this isIndirect
is specified and this CrlEntry
+ * has no certificate issuer CRL entry extension
+ * previousCertificateIssuer
is returned by
+ * {@link #getCertificateIssuer()}.
+ *
+ * @param c
+ * TbsCertificateList.CrlEntry object.
+ * @param isIndirect
+ * true
if the corresponding CRL is a indirect
+ * CRL.
+ * @param previousCertificateIssuer
+ * Certificate issuer of the previous CrlEntry.
+ */
+ public X509CrlEntry(
+ CrlEntry c,
+ bool isIndirect,
+ X509Name previousCertificateIssuer)
+ {
+ this.c = c;
+ this.isIndirect = isIndirect;
+ this.previousCertificateIssuer = previousCertificateIssuer;
+ this.certificateIssuer = loadCertificateIssuer();
+ }
+
+ private X509Name loadCertificateIssuer()
+ {
+ if (!isIndirect)
+ {
+ return null;
+ }
+
+ Asn1OctetString ext = GetExtensionValue(X509Extensions.CertificateIssuer);
+ if (ext == null)
+ {
+ return previousCertificateIssuer;
+ }
+
+ try
+ {
+ GeneralName[] names = GeneralNames.GetInstance(
+ X509ExtensionUtilities.FromExtensionValue(ext)).GetNames();
+
+ for (int i = 0; i < names.Length; i++)
+ {
+ if (names[i].TagNo == GeneralName.DirectoryName)
+ {
+ return X509Name.GetInstance(names[i].Name);
+ }
+ }
+ }
+ catch (Exception)
+ {
+ }
+
+ return null;
+ }
+
+ public X509Name GetCertificateIssuer()
+ {
+ return certificateIssuer;
+ }
+
+ protected override X509Extensions GetX509Extensions()
+ {
+ return c.Extensions;
+ }
+
+ public byte[] GetEncoded()
+ {
+ try
+ {
+ return c.GetDerEncoded();
+ }
+ catch (Exception e)
+ {
+ throw new CrlException(e.ToString());
+ }
+ }
+
+ public BigInteger SerialNumber
+ {
+ get { return c.UserCertificate.Value; }
+ }
+
+ public DateTime RevocationDate
+ {
+ get { return c.RevocationDate.ToDateTime(); }
+ }
+
+ public bool HasExtensions
+ {
+ get { return c.Extensions != null; }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder buf = new StringBuilder();
+ string nl = Platform.NewLine;
+
+ buf.Append(" userCertificate: ").Append(this.SerialNumber).Append(nl);
+ buf.Append(" revocationDate: ").Append(this.RevocationDate).Append(nl);
+ buf.Append(" certificateIssuer: ").Append(this.GetCertificateIssuer()).Append(nl);
+
+ X509Extensions extensions = c.Extensions;
+
+ if (extensions != null)
+ {
+ IEnumerator e = extensions.ExtensionOids.GetEnumerator();
+ if (e.MoveNext())
+ {
+ buf.Append(" crlEntryExtensions:").Append(nl);
+
+ do
+ {
+ DerObjectIdentifier oid = (DerObjectIdentifier)e.Current;
+ X509Extension ext = extensions.GetExtension(oid);
+
+ if (ext.Value != null)
+ {
+ Asn1Object obj = Asn1Object.FromByteArray(ext.Value.GetOctets());
+
+ buf.Append(" critical(")
+ .Append(ext.IsCritical)
+ .Append(") ");
+ try
+ {
+ if (oid.Equals(X509Extensions.ReasonCode))
+ {
+ buf.Append(new CrlReason(DerEnumerated.GetInstance(obj)));
+ }
+ else if (oid.Equals(X509Extensions.CertificateIssuer))
+ {
+ buf.Append("Certificate issuer: ").Append(
+ GeneralNames.GetInstance((Asn1Sequence)obj));
+ }
+ else
+ {
+ buf.Append(oid.Id);
+ buf.Append(" value = ").Append(Asn1Dump.DumpAsString(obj));
+ }
+ buf.Append(nl);
+ }
+ catch (Exception)
+ {
+ buf.Append(oid.Id);
+ buf.Append(" value = ").Append("*****").Append(nl);
+ }
+ }
+ else
+ {
+ buf.Append(nl);
+ }
+ }
+ while (e.MoveNext());
+ }
+ }
+
+ return buf.ToString();
+ }
+ }
+}
diff --git a/src/core/srcbc/x509/X509CrlParser.cs b/src/core/srcbc/x509/X509CrlParser.cs
new file mode 100644
index 0000000..5f7027d
--- /dev/null
+++ b/src/core/srcbc/x509/X509CrlParser.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.X509
+{
+ public class X509CrlParser
+ {
+ private static readonly PemParser PemCrlParser = new PemParser("CRL");
+
+ private readonly bool lazyAsn1;
+
+ private Asn1Set sCrlData;
+ private int sCrlDataObjectCount;
+ private Stream currentCrlStream;
+
+ public X509CrlParser()
+ : this(false)
+ {
+ }
+
+ public X509CrlParser(
+ bool lazyAsn1)
+ {
+ this.lazyAsn1 = lazyAsn1;
+ }
+
+ private X509Crl ReadPemCrl(
+ Stream inStream)
+ {
+ Asn1Sequence seq = PemCrlParser.ReadPemObject(inStream);
+
+ return seq == null
+ ? null
+ : CreateX509Crl(CertificateList.GetInstance(seq));
+ }
+
+ private X509Crl ReadDerCrl(
+ Asn1InputStream dIn)
+ {
+ Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject();
+
+ if (seq.Count > 1 && seq[0] is DerObjectIdentifier)
+ {
+ if (seq[0].Equals(PkcsObjectIdentifiers.SignedData))
+ {
+ sCrlData = SignedData.GetInstance(
+ Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Crls;
+
+ return GetCrl();
+ }
+ }
+
+ return CreateX509Crl(CertificateList.GetInstance(seq));
+ }
+
+ private X509Crl GetCrl()
+ {
+ if (sCrlData == null || sCrlDataObjectCount >= sCrlData.Count)
+ {
+ return null;
+ }
+
+ return CreateX509Crl(
+ CertificateList.GetInstance(
+ sCrlData[sCrlDataObjectCount++]));
+ }
+
+ protected virtual X509Crl CreateX509Crl(
+ CertificateList c)
+ {
+ return new X509Crl(c);
+ }
+
+ /// + * id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } + * + * KeyUsage ::= BIT STRING { + * digitalSignature (0), + * nonRepudiation (1), + * keyEncipherment (2), + * dataEncipherment (3), + * keyAgreement (4), + * keyCertSign (5), + * cRLSign (6), + * encipherOnly (7), + * decipherOnly (8) } + *+ */ + public class X509KeyUsage + : Asn1Encodable + { + public const int DigitalSignature = 1 << 7; + public const int NonRepudiation = 1 << 6; + public const int KeyEncipherment = 1 << 5; + public const int DataEncipherment = 1 << 4; + public const int KeyAgreement = 1 << 3; + public const int KeyCertSign = 1 << 2; + public const int CrlSign = 1 << 1; + public const int EncipherOnly = 1 << 0; + public const int DecipherOnly = 1 << 15; + + private readonly int usage; + + /** + * Basic constructor. + * + * @param usage - the bitwise OR of the Key Usage flags giving the + * allowed uses for the key. + * e.g. (X509KeyUsage.keyEncipherment | X509KeyUsage.dataEncipherment) + */ + public X509KeyUsage( + int usage) + { + this.usage = usage; + } + + public override Asn1Object ToAsn1Object() + { + return new KeyUsage(usage); + } + } +} diff --git a/src/core/srcbc/x509/X509SignatureUtil.cs b/src/core/srcbc/x509/X509SignatureUtil.cs new file mode 100644 index 0000000..9929e22 --- /dev/null +++ b/src/core/srcbc/x509/X509SignatureUtil.cs @@ -0,0 +1,128 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.X509 +{ + internal class X509SignatureUtilities + { + private static readonly Asn1Null derNull = DerNull.Instance; + + internal static void SetSignatureParameters( + ISigner signature, + Asn1Encodable parameters) + { + if (parameters != null && !derNull.Equals(parameters)) + { + // TODO Put back in +// AlgorithmParameters sigParams = AlgorithmParameters.GetInstance(signature.getAlgorithm()); +// +// try +// { +// sigParams.Init(parameters.ToAsn1Object().GetDerEncoded()); +// } +// catch (IOException e) +// { +// throw new SignatureException("IOException decoding parameters: " + e.Message); +// } +// +// if (signature.getAlgorithm().EndsWith("MGF1")) +// { +// try +// { +// signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class)); +// } +// catch (GeneralSecurityException e) +// { +// throw new SignatureException("Exception extracting parameters: " + e.Message); +// } +// } + } + } + + internal static string GetSignatureName( + AlgorithmIdentifier sigAlgId) + { + Asn1Encodable parameters = sigAlgId.Parameters; + + if (parameters != null && !derNull.Equals(parameters)) + { + if (sigAlgId.ObjectID.Equals(PkcsObjectIdentifiers.IdRsassaPss)) + { + RsassaPssParameters rsaParams = RsassaPssParameters.GetInstance(parameters); + + return GetDigestAlgName(rsaParams.HashAlgorithm.ObjectID) + "withRSAandMGF1"; + } + if (sigAlgId.ObjectID.Equals(X9ObjectIdentifiers.ECDsaWithSha2)) + { + Asn1Sequence ecDsaParams = Asn1Sequence.GetInstance(parameters); + + return GetDigestAlgName((DerObjectIdentifier)ecDsaParams[0]) + "withECDSA"; + } + } + + return sigAlgId.ObjectID.Id; + } + + /** + * Return the digest algorithm using one of the standard JCA string + * representations rather than the algorithm identifier (if possible). + */ + private static string GetDigestAlgName( + DerObjectIdentifier digestAlgOID) + { + if (PkcsObjectIdentifiers.MD5.Equals(digestAlgOID)) + { + return "MD5"; + } + else if (OiwObjectIdentifiers.IdSha1.Equals(digestAlgOID)) + { + return "SHA1"; + } + else if (NistObjectIdentifiers.IdSha224.Equals(digestAlgOID)) + { + return "SHA224"; + } + else if (NistObjectIdentifiers.IdSha256.Equals(digestAlgOID)) + { + return "SHA256"; + } + else if (NistObjectIdentifiers.IdSha384.Equals(digestAlgOID)) + { + return "SHA384"; + } + else if (NistObjectIdentifiers.IdSha512.Equals(digestAlgOID)) + { + return "SHA512"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD128.Equals(digestAlgOID)) + { + return "RIPEMD128"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD160.Equals(digestAlgOID)) + { + return "RIPEMD160"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD256.Equals(digestAlgOID)) + { + return "RIPEMD256"; + } + else if (CryptoProObjectIdentifiers.GostR3411.Equals(digestAlgOID)) + { + return "GOST3411"; + } + else + { + return digestAlgOID.Id; + } + } + } +} diff --git a/src/core/srcbc/x509/X509Utilities.cs b/src/core/srcbc/x509/X509Utilities.cs new file mode 100644 index 0000000..6b4cd62 --- /dev/null +++ b/src/core/srcbc/x509/X509Utilities.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.X509 +{ + internal class X509Utilities + { + private static readonly Hashtable algorithms = new Hashtable(); + private static readonly Hashtable exParams = new Hashtable(); + private static readonly ISet noParams = new HashSet(); + + static X509Utilities() + { + algorithms.Add("MD2WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD2WITHRSA", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD5WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("MD5WITHRSA", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("SHA1WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA1WITHRSA", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA1WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA224WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA256WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA384WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA512WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("SHA1WITHDSA", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("DSAWITHSHA1", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224); + algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256); + algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224); + algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256); + algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384); + algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512); + algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512); + noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1); + noParams.Add(NistObjectIdentifiers.DsaWithSha224); + noParams.Add(NistObjectIdentifiers.DsaWithSha256); + + // + // RFC 4491 + // + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // explicit params + // + AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); + exParams.Add("SHA1WITHRSAANDMGF1", CreatePssParams(sha1AlgId, 20)); + + AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha224, DerNull.Instance); + exParams.Add("SHA224WITHRSAANDMGF1", CreatePssParams(sha224AlgId, 28)); + + AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance); + exParams.Add("SHA256WITHRSAANDMGF1", CreatePssParams(sha256AlgId, 32)); + + AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance); + exParams.Add("SHA384WITHRSAANDMGF1", CreatePssParams(sha384AlgId, 48)); + + AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance); + exParams.Add("SHA512WITHRSAANDMGF1", CreatePssParams(sha512AlgId, 64)); + } + + private static RsassaPssParameters CreatePssParams( + AlgorithmIdentifier hashAlgId, + int saltSize) + { + return new RsassaPssParameters( + hashAlgId, + new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, hashAlgId), + new DerInteger(saltSize), + new DerInteger(1)); + } + + internal static DerObjectIdentifier GetAlgorithmOid( + string algorithmName) + { + algorithmName = algorithmName.ToUpper(CultureInfo.InvariantCulture); + + if (algorithms.ContainsKey(algorithmName)) + { + return (DerObjectIdentifier) algorithms[algorithmName]; + } + + return new DerObjectIdentifier(algorithmName); + } + + internal static AlgorithmIdentifier GetSigAlgID( + DerObjectIdentifier sigOid, + string algorithmName) + { + if (noParams.Contains(sigOid)) + { + return new AlgorithmIdentifier(sigOid); + } + + algorithmName = algorithmName.ToUpper(CultureInfo.InvariantCulture); + + if (exParams.ContainsKey(algorithmName)) + { + return new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]); + } + + return new AlgorithmIdentifier(sigOid, DerNull.Instance); + } + + internal static IEnumerable GetAlgNames() + { + return new EnumerableProxy(algorithms.Keys); + } + + internal static byte[] GetSignatureForObject( + DerObjectIdentifier sigOid, // TODO Redundant now? + string sigName, + AsymmetricKeyParameter privateKey, + SecureRandom random, + Asn1Encodable ae) + { + if (sigOid == null) + throw new ArgumentNullException("sigOid"); + + ISigner sig = SignerUtilities.GetSigner(sigName); + + if (random != null) + { + sig.Init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.Init(true, privateKey); + } + + byte[] encoded = ae.GetDerEncoded(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + return sig.GenerateSignature(); + } + } +} diff --git a/src/core/srcbc/x509/X509V1CertificateGenerator.cs b/src/core/srcbc/x509/X509V1CertificateGenerator.cs new file mode 100644 index 0000000..ed67d49 --- /dev/null +++ b/src/core/srcbc/x509/X509V1CertificateGenerator.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509 +{ + ///
Selector
like implementation to select
+ * attribute certificates from a given set of criteria.
+ *
+ * @see org.bouncycastle.x509.X509AttributeCertificate
+ * @see org.bouncycastle.x509.X509Store
+ */
+ public class X509AttrCertStoreSelector
+ : IX509Selector
+ {
+ // TODO: name constraints???
+
+ private IX509AttributeCertificate attributeCert;
+ private DateTimeObject attributeCertificateValid;
+ private AttributeCertificateHolder holder;
+ private AttributeCertificateIssuer issuer;
+ private BigInteger serialNumber;
+ private ISet targetNames = new HashSet();
+ private ISet targetGroups = new HashSet();
+
+ public X509AttrCertStoreSelector()
+ {
+ }
+
+ private X509AttrCertStoreSelector(
+ X509AttrCertStoreSelector o)
+ {
+ this.attributeCert = o.attributeCert;
+ this.attributeCertificateValid = o.attributeCertificateValid;
+ this.holder = o.holder;
+ this.issuer = o.issuer;
+ this.serialNumber = o.serialNumber;
+ this.targetGroups = new HashSet(o.targetGroups);
+ this.targetNames = new HashSet(o.targetNames);
+ }
+
+ /// true
if the object matches this selector.X509AttributeCertificate
+ * must contain at least one of the specified target names.
+ * + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + *
+ * + * @param name The name as a GeneralName (notnull
)
+ */
+ public void AddTargetName(
+ GeneralName name)
+ {
+ targetNames.Add(name);
+ }
+
+ /**
+ * Adds a target name criterion for the attribute certificate to the target
+ * information extension criteria. The X509AttributeCertificate
+ * must contain at least one of the specified target names.
+ * + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + *
+ * + * @param name a byte array containing the name in ASN.1 DER encoded form of a GeneralName + * @throws IOException if a parsing error occurs. + */ + public void AddTargetName( + byte[] name) + { + AddTargetName(GeneralName.GetInstance(Asn1Object.FromByteArray(name))); + } + + /** + * Adds a collection with target names criteria. Ifnull
is
+ * given any will do.
+ * + * The collection consists of either GeneralName objects or byte[] arrays representing + * DER encoded GeneralName structures. + *
+ * + * @param names A collection of target names. + * @throws IOException if a parsing error occurs. + * @see #AddTargetName(byte[]) + * @see #AddTargetName(GeneralName) + */ + public void SetTargetNames( + IEnumerable names) + { + targetNames = ExtractGeneralNames(names); + } + + /** + * Gets the target names. The collection consists ofList
s
+ * made up of an Integer
in the first entry and a DER encoded
+ * byte array or a String
in the second entry.
+ * The returned collection is immutable.
+ * + * @return The collection of target names + * @see #setTargetNames(Collection) + */ + public IEnumerable GetTargetNames() + { + return new EnumerableProxy(targetNames); + } + + /** + * Adds a target group criterion for the attribute certificate to the target + * information extension criteria. TheX509AttributeCertificate
+ * must contain at least one of the specified target groups.
+ * + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + *
+ * + * @param group The group as GeneralName form (notnull
)
+ */
+ public void AddTargetGroup(
+ GeneralName group)
+ {
+ targetGroups.Add(group);
+ }
+
+ /**
+ * Adds a target group criterion for the attribute certificate to the target
+ * information extension criteria. The X509AttributeCertificate
+ * must contain at least one of the specified target groups.
+ * + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + *
+ * + * @param name a byte array containing the group in ASN.1 DER encoded form of a GeneralName + * @throws IOException if a parsing error occurs. + */ + public void AddTargetGroup( + byte[] name) + { + AddTargetGroup(GeneralName.GetInstance(Asn1Object.FromByteArray(name))); + } + + /** + * Adds a collection with target groups criteria. Ifnull
is
+ * given any will do.
+ *
+ * The collection consists of GeneralName
objects or byte[]
+ * representing DER encoded GeneralNames.
+ *
List
s
+ * made up of an Integer
in the first entry and a DER encoded
+ * byte array or a String
in the second entry.
+ * The returned collection is immutable.
+ * + * @return The collection of target groups. + * @see #setTargetGroups(Collection) + */ + public IEnumerable GetTargetGroups() + { + return new EnumerableProxy(targetGroups); + } + + private ISet ExtractGeneralNames( + IEnumerable names) + { + ISet result = new HashSet(); + + if (names != null) + { + foreach (object o in names) + { + if (o is GeneralName) + { + result.Add(o); + } + else + { + result.Add(GeneralName.GetInstance(Asn1Object.FromByteArray((byte[]) o))); + } + } + } + + return result; + } + } +} diff --git a/src/core/srcbc/x509/store/X509CertPairStoreSelector.cs b/src/core/srcbc/x509/store/X509CertPairStoreSelector.cs new file mode 100644 index 0000000..86f6c5c --- /dev/null +++ b/src/core/srcbc/x509/store/X509CertPairStoreSelector.cs @@ -0,0 +1,92 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + ///IX509Selector
implementation to select
+ /// certificate pairs, which are e.g. used for cross certificates. The set of
+ /// criteria is given from two X509CertStoreSelector
objects,
+ /// each of which, if present, must match the respective component of a pair.
+ /// X509CertificatePair
, this method
+ /// returns false
.
+ /// X509CertificatePair
to be tested.
+ /// true
if the object matches this selector.ISet
of DerObjectIdentifier
objects.
+ /// X509Store
s.+ /// The collection is copied. + ///
+ ///ICollection
.ICollection
of X509Name
objects
+ /// null
is specified, then no such
+ * optional information is provided.
+ *
+ * @param attrCert the IX509AttributeCertificate
being checked (or
+ * null
)
+ * @see #getAttrCertificateChecking()
+ */
+ public IX509AttributeCertificate AttrCertChecking
+ {
+ get { return attrCertChecking; }
+ set { this.attrCertChecking = value; }
+ }
+
+ /**
+ * If true
only complete CRLs are returned. Defaults to
+ * false
.
+ *
+ * @return true
if only complete CRLs are returned.
+ */
+ public bool CompleteCrlEnabled
+ {
+ get { return completeCrlEnabled; }
+ set { this.completeCrlEnabled = value; }
+ }
+
+ /**
+ * Returns if this selector must match CRLs with the delta CRL indicator
+ * extension set. Defaults to false
.
+ *
+ * @return Returns true
if only CRLs with the delta CRL
+ * indicator extension are selected.
+ */
+ public bool DeltaCrlIndicatorEnabled
+ {
+ get { return deltaCrlIndicatorEnabled; }
+ set { this.deltaCrlIndicatorEnabled = value; }
+ }
+
+ /**
+ * The issuing distribution point.
+ * + * The issuing distribution point extension is a CRL extension which + * identifies the scope and the distribution point of a CRL. The scope + * contains among others information about revocation reasons contained in + * the CRL. Delta CRLs and complete CRLs must have matching issuing + * distribution points.
+ *+ * The byte array is cloned to protect against subsequent modifications.
+ *+ * You must also enable or disable this criteria with + * {@link #setIssuingDistributionPointEnabled(bool)}.
+ * + * @param issuingDistributionPoint The issuing distribution point to set. + * This is the DER encoded OCTET STRING extension value. + * @see #getIssuingDistributionPoint() + */ + public byte[] IssuingDistributionPoint + { + get { return Arrays.Clone(issuingDistributionPoint); } + set { this.issuingDistributionPoint = Arrays.Clone(value); } + } + + /** + * Whether the issuing distribution point criteria should be applied. + * Defaults tofalse
.
+ * + * You may also set the issuing distribution point criteria if not a missing + * issuing distribution point should be assumed.
+ * + * @return Returns if the issuing distribution point check is enabled. + */ + public bool IssuingDistributionPointEnabled + { + get { return issuingDistributionPointEnabled; } + set { this.issuingDistributionPointEnabled = value; } + } + + /** + * The maximum base CRL number. Defaults tonull
.
+ *
+ * @return Returns the maximum base CRL number.
+ * @see #setMaxBaseCRLNumber(BigInteger)
+ */
+ public BigInteger MaxBaseCrlNumber
+ {
+ get { return maxBaseCrlNumber; }
+ set { this.maxBaseCrlNumber = value; }
+ }
+
+ public virtual bool Match(
+ object obj)
+ {
+ X509Crl c = obj as X509Crl;
+
+ if (c == null)
+ return false;
+
+ if (dateAndTime != null)
+ {
+ DateTime dt = dateAndTime.Value;
+ DateTime tu = c.ThisUpdate;
+ DateTimeObject nu = c.NextUpdate;
+
+ if (dt.CompareTo(tu) < 0 || nu == null || dt.CompareTo(nu.Value) >= 0)
+ return false;
+ }
+
+ if (issuers != null)
+ {
+ X509Name i = c.IssuerDN;
+
+ bool found = false;
+
+ foreach (X509Name issuer in issuers)
+ {
+ if (issuer.Equivalent(i))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return false;
+ }
+
+ if (maxCrlNumber != null || minCrlNumber != null)
+ {
+ Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.CrlNumber);
+ if (extVal == null)
+ return false;
+
+ BigInteger cn = CrlNumber.GetInstance(
+ X509ExtensionUtilities.FromExtensionValue(extVal)).PositiveValue;
+
+ if (maxCrlNumber != null && cn.CompareTo(maxCrlNumber) > 0)
+ return false;
+
+ if (minCrlNumber != null && cn.CompareTo(minCrlNumber) < 0)
+ return false;
+ }
+
+ DerInteger dci = null;
+ try
+ {
+ Asn1OctetString bytes = c.GetExtensionValue(X509Extensions.DeltaCrlIndicator);
+ if (bytes != null)
+ {
+ dci = DerInteger.GetInstance(X509ExtensionUtilities.FromExtensionValue(bytes));
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ if (dci == null)
+ {
+ if (DeltaCrlIndicatorEnabled)
+ return false;
+ }
+ else
+ {
+ if (CompleteCrlEnabled)
+ return false;
+
+ if (maxBaseCrlNumber != null && dci.PositiveValue.CompareTo(maxBaseCrlNumber) > 0)
+ return false;
+ }
+
+ if (issuingDistributionPointEnabled)
+ {
+ Asn1OctetString idp = c.GetExtensionValue(X509Extensions.IssuingDistributionPoint);
+ if (issuingDistributionPoint == null)
+ {
+ if (idp != null)
+ return false;
+ }
+ else
+ {
+ if (!Arrays.AreEqual(idp.GetOctets(), issuingDistributionPoint))
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/core/srcbc/x509/store/X509StoreException.cs b/src/core/srcbc/x509/store/X509StoreException.cs
new file mode 100644
index 0000000..cd6e47d
--- /dev/null
+++ b/src/core/srcbc/x509/store/X509StoreException.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Org.BouncyCastle.X509.Store
+{
+ public class X509StoreException
+ : Exception
+ {
+ public X509StoreException()
+ {
+ }
+
+ public X509StoreException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public X509StoreException(
+ string message,
+ Exception e)
+ : base(message, e)
+ {
+ }
+ }
+}
diff --git a/src/core/srcbc/x509/store/X509StoreFactory.cs b/src/core/srcbc/x509/store/X509StoreFactory.cs
new file mode 100644
index 0000000..2a2c818
--- /dev/null
+++ b/src/core/srcbc/x509/store/X509StoreFactory.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Globalization;
+
+namespace Org.BouncyCastle.X509.Store
+{
+ public sealed class X509StoreFactory
+ {
+ private X509StoreFactory()
+ {
+ }
+
+ public static IX509Store Create(
+ string type,
+ IX509StoreParameters parameters)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ string[] parts = type.ToUpper(CultureInfo.InvariantCulture).Split('/');
+
+ if (parts.Length < 2)
+ throw new ArgumentException("type");
+
+
+ switch (parts[0])
+ {
+ case "ATTRIBUTECERTIFICATE":
+ case "CERTIFICATE":
+ case "CERTIFICATEPAIR":
+ case "CRL":
+ {
+ if (parts[1] == "COLLECTION")
+ {
+ X509CollectionStoreParameters p = (X509CollectionStoreParameters) parameters;
+ return new X509CollectionStore(p.GetCollection());
+ }
+ break;
+ }
+ }
+
+ throw new NoSuchStoreException("X.509 store type '" + type + "' not available.");
+ }
+ }
+}