summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorTim Whittington <bc@whittington.net.nz>2013-10-11 20:27:51 +1300
committerTim Whittington <bc@whittington.net.nz>2013-10-20 21:29:17 +1300
commit201d86c4ab73d81605402d2b0cae15106f9e372d (patch)
treeb6fb1854c0c46a06201df6117abf5fc16256e1f1 /crypto
parentPort of Threefish implementation from bc-java. (diff)
downloadBouncyCastle.NET-ed25519-201d86c4ab73d81605402d2b0cae15106f9e372d.tar.xz
Port SkeinDigest and SkeinMac from bc-java.
Skein digest and Mac in 256/512/1024 bit state sizes (and arbitrary byte level output size), with unit tests.
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto.mdp6
-rw-r--r--crypto/src/crypto/digests/SkeinDigest.cs117
-rw-r--r--crypto/src/crypto/digests/SkeinEngine.cs803
-rw-r--r--crypto/src/crypto/macs/SkeinMac.cs117
-rw-r--r--crypto/src/crypto/parameters/SkeinParameters.cs285
-rw-r--r--crypto/src/util/Arrays.cs40
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs2
-rw-r--r--crypto/test/src/crypto/test/SkeinDigestTest.cs303
-rw-r--r--crypto/test/src/crypto/test/SkeinMacTest.cs174
9 files changed, 1847 insertions, 0 deletions
diff --git a/crypto/crypto.mdp b/crypto/crypto.mdp
index b9ec2f9c2..f3589858f 100644
--- a/crypto/crypto.mdp
+++ b/crypto/crypto.mdp
@@ -2302,6 +2302,12 @@
     <File subtype="Code" buildaction="Compile" name="test/src/crypto/test/Threefish256Test.cs" />
     <File subtype="Code" buildaction="Compile" name="test/src/crypto/test/Threefish512Test.cs" />
     <File subtype="Code" buildaction="Compile" name="test/src/crypto/test/Threefish1024Test.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/crypto/parameters/SkeinParameters.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/crypto/digests/SkeinDigest.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/crypto/digests/SkeinEngine.cs" />
+    <File subtype="Code" buildaction="Compile" name="test/src/crypto/test/SkeinDigestTest.cs" />
+    <File subtype="Code" buildaction="Compile" name="test/src/crypto/test/SkeinMacTest.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/crypto/macs/SkeinMac.cs" />
   </Contents>
   <References>
     <ProjectReference type="Assembly" localcopy="True" refto="test/lib/nunit.core.dll" />
diff --git a/crypto/src/crypto/digests/SkeinDigest.cs b/crypto/src/crypto/digests/SkeinDigest.cs
new file mode 100644
index 000000000..b24fb3995
--- /dev/null
+++ b/crypto/src/crypto/digests/SkeinDigest.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+
+	/// <summary>
+	/// Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes,
+	/// based on the <see cref="Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher.
+	/// </summary>
+	/// <remarks>
+	/// This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+	/// competition in October 2010.
+	/// <p/>
+	/// Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+	/// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+	/// </remarks>
+	/// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinEngine"/>
+	/// <seealso cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/>
+	public class SkeinDigest
+		: IDigest//, IMemoable
+	{
+		/// <summary>
+		/// 256 bit block size - Skein-256
+		/// </summary>
+		public const int SKEIN_256 = SkeinEngine.SKEIN_256;
+		/// <summary>
+		/// 512 bit block size - Skein-512
+		/// </summary>
+		public const int SKEIN_512 = SkeinEngine.SKEIN_512;
+		/// <summary>
+		/// 1024 bit block size - Skein-1024
+		/// </summary>
+		public const int SKEIN_1024 = SkeinEngine.SKEIN_1024;
+
+		private readonly SkeinEngine engine;
+
+		/// <summary>
+		/// Constructs a Skein digest with an internal state size and output size.
+		/// </summary>
+		/// <param name="stateSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or
+		///                       <see cref="SKEIN_1024"/>.</param>
+		/// <param name="digestSizeBits">the output/digest size to produce in bits, which must be an integral number of
+		///                      bytes.</param>
+		public SkeinDigest(int stateSizeBits, int digestSizeBits)
+		{
+			this.engine = new SkeinEngine(stateSizeBits, digestSizeBits);
+			Init(null);
+		}
+
+		public SkeinDigest(SkeinDigest digest)
+		{
+			this.engine = new SkeinEngine(digest.engine);
+		}
+
+		//	    public void Reset(IMemoable other)
+		//	    {
+		//	        SkeinDigest d = (SkeinDigest)other;
+		//	        engine.reset(d.engine);
+		//	    }
+		//
+		//	    public IMemoable copy()
+		//	    {
+		//	        return new SkeinDigest(this);
+		//	    }
+
+		public String AlgorithmName
+		{
+			get { return "Skein-" + (engine.BlockSize * 8) + "-" + (engine.OutputSize * 8); }
+		}
+
+		public int GetDigestSize()
+		{
+			return engine.OutputSize;
+		}
+
+		public int GetByteLength()
+		{
+			return engine.BlockSize;
+		}
+
+		/// <summary>
+		/// Optionally initialises the Skein digest with the provided parameters.
+		/// </summary>
+		/// See <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"></see> for details on the parameterisation of the Skein hash function.
+		/// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param>
+		public void Init(SkeinParameters parameters)
+		{
+			engine.Init(parameters);
+		}
+
+		public void Reset()
+		{
+			engine.Reset();
+		}
+
+		public void Update(byte inByte)
+		{
+			engine.Update(inByte);
+		}
+
+		public void BlockUpdate(byte[] inBytes, int inOff, int len)
+		{
+			engine.Update(inBytes, inOff, len);
+		}
+
+		public int DoFinal(byte[] outBytes, int outOff)
+		{
+			return engine.DoFinal(outBytes, outOff);
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/crypto/src/crypto/digests/SkeinEngine.cs b/crypto/src/crypto/digests/SkeinEngine.cs
new file mode 100644
index 000000000..94529bcf6
--- /dev/null
+++ b/crypto/src/crypto/digests/SkeinEngine.cs
@@ -0,0 +1,803 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+
+	/// <summary>
+	/// Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block
+	/// sizes, based on the <see cref="Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher.
+	/// </summary>
+	/// <remarks>
+	/// This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+	/// competition in October 2010.
+	/// <p/>
+	/// Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+	/// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+	/// <p/>
+	/// This implementation is the basis for <see cref="Org.BouncyCastle.Crypto.Digests.SkeinDigest"/> and <see cref="Org.BouncyCastle.Crypto.Macs.SkeinMac"/>, implementing the
+	/// parameter based configuration system that allows Skein to be adapted to multiple applications. <br>
+	/// Initialising the engine with <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> allows standard and arbitrary parameters to
+	/// be applied during the Skein hash function.
+	/// <p/>
+	/// Implemented:
+	/// <ul>
+	/// <li>256, 512 and 1024 bit internal states.</li>
+	/// <li>Full 96 bit input length.</li>
+	/// <li>Parameters defined in the Skein specification, and arbitrary other pre and post message
+	/// parameters.</li>
+	/// <li>Arbitrary output size in 1 byte intervals.</li>
+	/// </ul>
+	/// <p/>
+	/// Not implemented:
+	/// <ul>
+	/// <li>Sub-byte length input (bit padding).</li>
+	/// <li>Tree hashing.</li>
+	/// </ul>
+	/// </remarks>
+	/// <seealso cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/>
+	public class SkeinEngine
+		//	    : IMemoable
+	{
+		/// <summary>
+		/// 256 bit block size - Skein-256
+		/// </summary>
+		public const int SKEIN_256 = ThreefishEngine.BLOCKSIZE_256;
+		/// <summary>
+		/// 512 bit block size - Skein-512
+		/// </summary>
+		public const int SKEIN_512 = ThreefishEngine.BLOCKSIZE_512;
+		/// <summary>
+		/// 1024 bit block size - Skein-1024
+		/// </summary>
+		public const int SKEIN_1024 = ThreefishEngine.BLOCKSIZE_1024;
+
+		// Minimal at present, but more complex when tree hashing is implemented
+		private class Configuration
+		{
+			private byte[] bytes = new byte[32];
+
+			public Configuration(long outputSizeBits)
+			{
+				// 0..3 = ASCII SHA3
+				bytes[0] = (byte)'S';
+				bytes[1] = (byte)'H';
+				bytes[2] = (byte)'A';
+				bytes[3] = (byte)'3';
+
+				// 4..5 = version number in LSB order
+				bytes[4] = 1;
+				bytes[5] = 0;
+
+				// 8..15 = output length
+				ThreefishEngine.WordToBytes((ulong)outputSizeBits, bytes, 8);
+			}
+
+			public byte[] Bytes
+			{
+				get { return bytes; }
+			}
+
+		}
+
+		public class Parameter
+		{
+			private int type;
+			private byte[] value;
+
+			public Parameter(int type, byte[] value)
+			{
+				this.type = type;
+				this.value = value;
+			}
+
+			public int Type
+			{
+				get { return type; }
+			}
+
+			public byte[] Value
+			{
+				get { return value; }
+			}
+
+		}
+
+		/**
+	     * The parameter type for the Skein key.
+	     */
+		private const int PARAM_TYPE_KEY = 0;
+
+		/**
+	     * The parameter type for the Skein configuration block.
+	     */
+		private const int PARAM_TYPE_CONFIG = 4;
+
+		/**
+	     * The parameter type for the message.
+	     */
+		private const int PARAM_TYPE_MESSAGE = 48;
+
+		/**
+	     * The parameter type for the output transformation.
+	     */
+		private const int PARAM_TYPE_OUTPUT = 63;
+
+		/**
+	     * Precalculated UBI(CFG) states for common state/output combinations without key or other
+	     * pre-message params.
+	     */
+		private static readonly IDictionary INITIAL_STATES = Platform.CreateHashtable();
+
+		static SkeinEngine()
+		{
+			// From Appendix C of the Skein 1.3 NIST submission
+			InitialState(SKEIN_256, 128, new ulong[]{
+				0xe1111906964d7260UL,
+				0x883daaa77c8d811cUL,
+				0x10080df491960f7aUL,
+				0xccf7dde5b45bc1c2UL});
+
+			InitialState(SKEIN_256, 160, new ulong[]{
+				0x1420231472825e98UL,
+				0x2ac4e9a25a77e590UL,
+				0xd47a58568838d63eUL,
+				0x2dd2e4968586ab7dUL});
+
+			InitialState(SKEIN_256, 224, new ulong[]{
+				0xc6098a8c9ae5ea0bUL,
+				0x876d568608c5191cUL,
+				0x99cb88d7d7f53884UL,
+				0x384bddb1aeddb5deUL});
+
+			InitialState(SKEIN_256, 256, new ulong[]{
+				0xfc9da860d048b449UL,
+				0x2fca66479fa7d833UL,
+				0xb33bc3896656840fUL,
+				0x6a54e920fde8da69UL});
+
+			InitialState(SKEIN_512, 128, new ulong[]{
+				0xa8bc7bf36fbf9f52UL,
+				0x1e9872cebd1af0aaUL,
+				0x309b1790b32190d3UL,
+				0xbcfbb8543f94805cUL,
+				0x0da61bcd6e31b11bUL,
+				0x1a18ebead46a32e3UL,
+				0xa2cc5b18ce84aa82UL,
+				0x6982ab289d46982dUL});
+
+			InitialState(SKEIN_512, 160, new ulong[]{
+				0x28b81a2ae013bd91UL,
+				0xc2f11668b5bdf78fUL,
+				0x1760d8f3f6a56f12UL,
+				0x4fb747588239904fUL,
+				0x21ede07f7eaf5056UL,
+				0xd908922e63ed70b8UL,
+				0xb8ec76ffeccb52faUL,
+				0x01a47bb8a3f27a6eUL});
+
+			InitialState(SKEIN_512, 224, new ulong[]{
+				0xccd0616248677224UL,
+				0xcba65cf3a92339efUL,
+				0x8ccd69d652ff4b64UL,
+				0x398aed7b3ab890b4UL,
+				0x0f59d1b1457d2bd0UL,
+				0x6776fe6575d4eb3dUL,
+				0x99fbc70e997413e9UL,
+				0x9e2cfccfe1c41ef7UL});
+
+			InitialState(SKEIN_512, 384, new ulong[]{
+				0xa3f6c6bf3a75ef5fUL,
+				0xb0fef9ccfd84faa4UL,
+				0x9d77dd663d770cfeUL,
+				0xd798cbf3b468fddaUL,
+				0x1bc4a6668a0e4465UL,
+				0x7ed7d434e5807407UL,
+				0x548fc1acd4ec44d6UL,
+				0x266e17546aa18ff8UL});
+
+			InitialState(SKEIN_512, 512, new ulong[]{
+				0x4903adff749c51ceUL,
+				0x0d95de399746df03UL,
+				0x8fd1934127c79bceUL,
+				0x9a255629ff352cb1UL,
+				0x5db62599df6ca7b0UL,
+				0xeabe394ca9d5c3f4UL,
+				0x991112c71a75b523UL,
+				0xae18a40b660fcc33UL});
+		}
+
+		private static void InitialState(int blockSize, int outputSize, ulong[] state)
+		{
+			INITIAL_STATES.Add(VariantIdentifier(blockSize / 8, outputSize / 8), state);
+		}
+
+		private static int VariantIdentifier(int blockSizeBytes, int outputSizeBytes)
+		{
+			return (outputSizeBytes << 16) | blockSizeBytes;
+		}
+
+		private class UbiTweak
+		{
+			/**
+	         * Point at which position might overflow long, so switch to add with carry logic
+	         */
+			private const ulong LOW_RANGE = UInt64.MaxValue - UInt32.MaxValue;
+
+			/**
+	         * Bit 127 = final
+	         */
+			private const ulong T1_FINAL = 1UL << 63;
+
+			/**
+	         * Bit 126 = first
+	         */
+			private const ulong T1_FIRST = 1UL << 62;
+
+			/**
+	         * UBI uses a 128 bit tweak
+	         */
+			private ulong[] tweak = new ulong[2];
+
+			/**
+	         * Whether 64 bit position exceeded
+	         */
+			private bool extendedPosition;
+
+			public UbiTweak()
+			{
+				Reset();
+			}
+
+			public void Reset(UbiTweak tweak)
+			{
+				this.tweak = Arrays.Clone(tweak.tweak, this.tweak);
+				this.extendedPosition = tweak.extendedPosition;
+			}
+
+			public void Reset()
+			{
+				tweak[0] = 0;
+				tweak[1] = 0;
+				extendedPosition = false;
+				First = true;
+			}
+
+			public uint Type 
+			{
+				get 
+				{
+					return (uint)((tweak[1] >> 56) & 0x3FUL);
+				}
+
+				set 
+				{
+					// Bits 120..125 = type
+					tweak[1] = (tweak[1] & 0xFFFFFFC000000000UL) | ((value & 0x3FUL) << 56);
+				}
+			}
+
+			public bool First
+			{
+				get
+				{
+					return ((tweak[1] & T1_FIRST) != 0);
+				}
+				set
+				{
+					if (value)
+					{
+						tweak[1] |= T1_FIRST;
+					}
+					else
+					{
+						tweak[1] &= ~T1_FIRST;
+					}
+				}
+			}
+
+			public bool Final
+			{
+				get
+				{
+					return ((tweak[1] & T1_FINAL) != 0);
+				}
+				set
+				{
+					if (value)
+					{
+						tweak[1] |= T1_FINAL;
+					}
+					else
+					{
+						tweak[1] &= ~T1_FINAL;
+					}
+				}
+			}
+
+			/**
+	         * Advances the position in the tweak by the specified value.
+	         */
+			public void AdvancePosition(int advance)
+			{
+				// Bits 0..95 = position
+				if (extendedPosition)
+				{
+					ulong[] parts = new ulong[3];
+					parts[0] = tweak[0] & 0xFFFFFFFFUL;
+					parts[1] = (tweak[0] >> 32) & 0xFFFFFFFFUL;
+					parts[2] = tweak[1] & 0xFFFFFFFFUL;
+
+					ulong carry = (ulong)advance;
+					for (int i = 0; i < parts.Length; i++)
+					{
+						carry += parts[i];
+						parts[i] = carry;
+						carry >>= 32;
+					}
+					tweak[0] = ((parts[1] & 0xFFFFFFFFUL) << 32) | (parts[0] & 0xFFFFFFFFUL);
+					tweak[1] = (tweak[1] & 0xFFFFFFFF00000000UL) | (parts[2] & 0xFFFFFFFFUL);
+				}
+				else
+				{
+					ulong position = tweak[0];
+					position += (uint)advance;
+					tweak[0] = position;
+					if (position > LOW_RANGE)
+					{
+						extendedPosition = true;
+					}
+				}
+			}
+
+			public ulong[] GetWords()
+			{
+				return tweak;
+			}
+
+			public override string ToString()
+			{
+				return Type + " first: " + First + ", final: " + Final;
+			}
+
+		}
+
+		/**
+	     * The Unique Block Iteration chaining mode.
+	     */
+		// TODO: This might be better as methods...
+		private class UBI
+		{
+			private readonly UbiTweak tweak = new UbiTweak();
+
+			private readonly SkeinEngine engine;
+
+			/**
+	         * Buffer for the current block of message data
+	         */
+			private byte[] currentBlock;
+
+			/**
+	         * Offset into the current message block
+	         */
+			private int currentOffset;
+
+			/**
+	         * Buffer for message words for feedback into encrypted block
+	         */
+			private ulong[] message;
+
+			public UBI(SkeinEngine engine, int blockSize)
+			{
+				this.engine = engine;
+				currentBlock = new byte[blockSize];
+				message = new ulong[currentBlock.Length / 8];
+			}
+
+			public void Reset(UBI ubi)
+			{
+				currentBlock = Arrays.Clone(ubi.currentBlock, currentBlock);
+				currentOffset = ubi.currentOffset;
+				message = Arrays.Clone(ubi.message, this.message);
+				tweak.Reset(ubi.tweak);
+			}
+
+			public void Reset(int type)
+			{
+				tweak.Reset();
+				tweak.Type = (uint)type;
+				currentOffset = 0;
+			}
+
+			public void Update(byte[] value, int offset, int len, ulong[] output)
+			{
+				/*
+	             * Buffer complete blocks for the underlying Threefish cipher, only flushing when there
+	             * are subsequent bytes (last block must be processed in doFinal() with final=true set).
+	             */
+				int copied = 0;
+				while (len > copied)
+				{
+					if (currentOffset == currentBlock.Length)
+					{
+						ProcessBlock(output);
+						tweak.First = false;
+						currentOffset = 0;
+					}
+
+					int toCopy = System.Math.Min((len - copied), currentBlock.Length - currentOffset);
+					Array.Copy(value, offset + copied, currentBlock, currentOffset, toCopy);
+					copied += toCopy;
+					currentOffset += toCopy;
+					tweak.AdvancePosition(toCopy);
+				}
+			}
+
+			private void ProcessBlock(ulong[] output)
+			{
+				engine.threefish.Init(true, engine.chain, tweak.GetWords());
+				for (int i = 0; i < message.Length; i++)
+				{
+					message[i] = ThreefishEngine.BytesToWord(currentBlock, i * 8);
+				}
+
+				engine.threefish.ProcessBlock(message, output);
+
+				for (int i = 0; i < output.Length; i++)
+				{
+					output[i] ^= message[i];
+				}
+			}
+
+			public void DoFinal(ulong[] output)
+			{
+				// Pad remainder of current block with zeroes
+				for (int i = currentOffset; i < currentBlock.Length; i++)
+				{
+					currentBlock[i] = 0;
+				}
+
+				tweak.Final = true;
+				ProcessBlock(output);
+			}
+
+		}
+
+		/**
+	     * Underlying Threefish tweakable block cipher
+	     */
+		private readonly ThreefishEngine threefish;
+
+		/**
+	     * Size of the digest output, in bytes
+	     */
+		private readonly int outputSizeBytes;
+
+		/**
+	     * The current chaining/state value
+	     */
+		private ulong[] chain;
+
+		/**
+	     * The initial state value
+	     */
+		private ulong[] initialState;
+
+		/**
+	     * The (optional) key parameter
+	     */
+		private byte[] key;
+
+		/**
+	     * Parameters to apply prior to the message
+	     */
+		private Parameter[] preMessageParameters;
+
+		/**
+	     * Parameters to apply after the message, but prior to output
+	     */
+		private Parameter[] postMessageParameters;
+
+		/**
+	     * The current UBI operation
+	     */
+		private readonly UBI ubi;
+
+		/**
+	     * Buffer for single byte update method
+	     */
+		private readonly byte[] singleByte = new byte[1];
+
+		/// <summary>
+		/// Constructs a Skein digest with an internal state size and output size.
+		/// </summary>
+		/// <param name="stateSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or
+		///                       <see cref="SKEIN_1024"/>.</param>
+		/// <param name="outputSizeBits">the output/digest size to produce in bits, which must be an integral number of
+		///                      bytes.</param>
+		public SkeinEngine(int blockSizeBits, int outputSizeBits)
+		{
+			if (outputSizeBits % 8 != 0)
+			{
+				throw new ArgumentException("Output size must be a multiple of 8 bits. :" + outputSizeBits);
+			}
+			// TODO: Prevent digest sizes > block size?
+			this.outputSizeBytes = outputSizeBits / 8;
+
+			this.threefish = new ThreefishEngine(blockSizeBits);
+			this.ubi = new UBI(this,threefish.GetBlockSize());
+		}
+
+		/// <summary>
+		/// Creates a SkeinEngine as an exact copy of an existing instance.
+		/// </summary>
+		public SkeinEngine(SkeinEngine engine)
+			: this(engine.BlockSize * 8, engine.OutputSize * 8)
+		{
+			CopyIn(engine);
+		}
+
+		private void CopyIn(SkeinEngine engine)
+		{
+			this.ubi.Reset(engine.ubi);
+			this.chain = Arrays.Clone(engine.chain, this.chain);
+			this.initialState = Arrays.Clone(engine.initialState, this.initialState);
+			this.key = Arrays.Clone(engine.key, this.key);
+			this.preMessageParameters = Clone(engine.preMessageParameters, this.preMessageParameters);
+			this.postMessageParameters = Clone(engine.postMessageParameters, this.postMessageParameters);
+		}
+
+		private static Parameter[] Clone(Parameter[] data, Parameter[] existing)
+		{
+			if (data == null)
+			{
+				return null;
+			}
+			if ((existing == null) || (existing.Length != data.Length))
+			{
+				existing = new Parameter[data.Length];
+			}
+			Array.Copy(data, 0, existing, 0, existing.Length);
+			return existing;
+		}
+
+		//	    public IMemoable Copy()
+		//	    {
+		//	        return new SkeinEngine(this);
+		//	    }
+		//
+		//	    public void Reset(IMemoable other)
+		//	    {
+		//	        SkeinEngine s = (SkeinEngine)other;
+		//	        if ((getBlockSize() != s.getBlockSize()) || (outputSizeBytes != s.outputSizeBytes))
+		//	        {
+		//	            throw new IllegalArgumentException("Incompatible parameters in provided SkeinEngine.");
+		//	        }
+		//	        copyIn(s);
+		//	    }
+
+		public int OutputSize
+		{
+			get { return outputSizeBytes; }
+		}
+
+		public int BlockSize
+		{
+			get { return threefish.GetBlockSize (); }
+		}
+
+		/// <summary>
+		/// Initialises the Skein engine with the provided parameters. See <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> for
+		/// details on the parameterisation of the Skein hash function.
+		/// </summary>
+		/// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param>
+		public void Init(SkeinParameters parameters)
+		{
+			this.chain = null;
+			this.key = null;
+			this.preMessageParameters = null;
+			this.postMessageParameters = null;
+
+			if (parameters != null)
+			{
+				byte[] key = parameters.GetKey();
+				if (key.Length < 16)
+				{
+					throw new ArgumentException("Skein key must be at least 128 bits.");
+				}
+				InitParams(parameters.GetParameters());
+			}
+			CreateInitialState();
+
+			// Initialise message block
+			UbiInit(PARAM_TYPE_MESSAGE);
+		}
+
+		private void InitParams(IDictionary parameters)
+		{
+			IEnumerator keys = parameters.Keys.GetEnumerator();
+			IList pre = Platform.CreateArrayList();
+			IList post = Platform.CreateArrayList();
+
+			while (keys.MoveNext())
+			{
+				int type = (int)keys.Current;
+				byte[] value = (byte[])parameters[type];
+
+				if (type == PARAM_TYPE_KEY)
+				{
+					this.key = value;
+				}
+				else if (type < PARAM_TYPE_MESSAGE)
+				{
+					pre.Add(new Parameter(type, value));
+				}
+				else
+				{
+					post.Add(new Parameter(type, value));
+				}
+			}
+			preMessageParameters = new Parameter[pre.Count];
+			pre.CopyTo(preMessageParameters, 0);
+			Array.Sort(preMessageParameters);
+
+			postMessageParameters = new Parameter[post.Count];
+			post.CopyTo(postMessageParameters, 0);
+			Array.Sort(postMessageParameters);
+		}
+
+		/**
+	     * Calculate the initial (pre message block) chaining state.
+	     */
+		private void CreateInitialState()
+		{
+			ulong[] precalc = (ulong[])INITIAL_STATES[VariantIdentifier(BlockSize, OutputSize)];
+			if ((key == null) && (precalc != null))
+			{
+				// Precalculated UBI(CFG)
+				chain = Arrays.Clone(precalc);
+			}
+			else
+			{
+				// Blank initial state
+				chain = new ulong[BlockSize / 8];
+
+				// Process key block
+				if (key != null)
+				{
+					UbiComplete(SkeinParameters.PARAM_TYPE_KEY, key);
+				}
+
+				// Process configuration block
+				UbiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).Bytes);
+			}
+
+			// Process additional pre-message parameters
+			if (preMessageParameters != null)
+			{
+				for (int i = 0; i < preMessageParameters.Length; i++)
+				{
+					Parameter param = preMessageParameters[i];
+					UbiComplete(param.Type, param.Value);
+				}
+			}
+			initialState = Arrays.Clone(chain);
+		}
+
+		/// <summary>
+		/// Reset the engine to the initial state (with the key and any pre-message parameters , ready to
+		/// accept message input.
+		/// </summary>
+		public void Reset()
+		{
+			Array.Copy(initialState, 0, chain, 0, chain.Length);
+
+			UbiInit(PARAM_TYPE_MESSAGE);
+		}
+
+		private void UbiComplete(int type, byte[] value)
+		{
+			UbiInit(type);
+			this.ubi.Update(value, 0, value.Length, chain);
+			UbiFinal();
+		}
+
+		private void UbiInit(int type)
+		{
+			this.ubi.Reset(type);
+		}
+
+		private void UbiFinal()
+		{
+			ubi.DoFinal(chain);
+		}
+
+		private void CheckInitialised()
+		{
+			if (this.ubi == null)
+			{
+				throw new ArgumentException("Skein engine is not initialised.");
+			}
+		}
+
+		public void Update(byte inByte)
+		{
+			singleByte[0] = inByte;
+			Update(singleByte, 0, 1);
+		}
+
+		public void Update(byte[] inBytes, int inOff, int len)
+		{
+			CheckInitialised();
+			ubi.Update(inBytes, inOff, len, chain);
+		}
+
+		public int DoFinal(byte[] outBytes, int outOff)
+		{
+			CheckInitialised();
+			if (outBytes.Length < (outOff + outputSizeBytes))
+			{
+				throw new DataLengthException("Output buffer is too short to hold output of " + outputSizeBytes + " bytes");
+			}
+
+			// Finalise message block
+			UbiFinal();
+
+			// Process additional post-message parameters
+			if (postMessageParameters != null)
+			{
+				for (int i = 0; i < postMessageParameters.Length; i++)
+				{
+					Parameter param = postMessageParameters[i];
+					UbiComplete(param.Type, param.Value);
+				}
+			}
+
+			// Perform the output transform
+			int blockSize = BlockSize;
+			int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize);
+			for (int i = 0; i < blocksRequired; i++)
+			{
+				int toWrite = System.Math.Min(blockSize, outputSizeBytes - (i * blockSize));
+				Output((ulong)i, outBytes, outOff + (i * blockSize), toWrite);
+			}
+
+			Reset();
+
+			return outputSizeBytes;
+		}
+
+		private void Output(ulong outputSequence, byte[] outBytes, int outOff, int outputBytes)
+		{
+			byte[] currentBytes = new byte[8];
+			ThreefishEngine.WordToBytes(outputSequence, currentBytes, 0);
+
+			// Output is a sequence of UBI invocations all of which use and preserve the pre-output
+			// state
+			ulong[] outputWords = new ulong[chain.Length];
+			UbiInit(PARAM_TYPE_OUTPUT);
+			this.ubi.Update(currentBytes, 0, currentBytes.Length, outputWords);
+			ubi.DoFinal(outputWords);
+
+			int wordsRequired = ((outputBytes + 8 - 1) / 8);
+			for (int i = 0; i < wordsRequired; i++)
+			{
+				int toWrite = System.Math.Min(8, outputBytes - (i * 8));
+				if (toWrite == 8)
+				{
+					ThreefishEngine.WordToBytes(outputWords[i], outBytes, outOff + (i * 8));
+				}
+				else
+				{
+					ThreefishEngine.WordToBytes(outputWords[i], currentBytes, 0);
+					Array.Copy(currentBytes, 0, outBytes, outOff + (i * 8), toWrite);
+				}
+			}
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/crypto/src/crypto/macs/SkeinMac.cs b/crypto/src/crypto/macs/SkeinMac.cs
new file mode 100644
index 000000000..1d61a41ca
--- /dev/null
+++ b/crypto/src/crypto/macs/SkeinMac.cs
@@ -0,0 +1,117 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+
+	/// <summary>
+	/// Implementation of the Skein parameterised MAC function in 256, 512 and 1024 bit block sizes,
+	/// based on the <see cref="Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher.
+	/// </summary>
+	/// <remarks>
+	/// This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+	/// competition in October 2010.
+	/// <p/>
+	/// Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+	/// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+	/// </remarks>
+	/// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinEngine"/>
+	/// <seealso cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/>
+	public class SkeinMac
+		: IMac
+	{
+		/// <summary>
+		/// 256 bit block size - Skein-256
+		/// </summary>
+		public const int SKEIN_256 = SkeinEngine.SKEIN_256;
+		/// <summary>
+		/// 512 bit block size - Skein-512
+		/// </summary>
+		public const int SKEIN_512 = SkeinEngine.SKEIN_512;
+		/// <summary>
+		/// 1024 bit block size - Skein-1024
+		/// </summary>
+		public const int SKEIN_1024 = SkeinEngine.SKEIN_1024;
+
+		private readonly SkeinEngine engine;
+
+		/// <summary>
+		/// Constructs a Skein MAC with an internal state size and output size.
+		/// </summary>
+		/// <param name="stateSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or
+		///                       <see cref="SKEIN_1024"/>.</param>
+		/// <param name="digestSizeBits">the output/MAC size to produce in bits, which must be an integral number of
+		///                      bytes.</param>
+		public SkeinMac(int stateSizeBits, int digestSizeBits)
+		{
+			this.engine = new SkeinEngine(stateSizeBits, digestSizeBits);
+		}
+
+		public SkeinMac(SkeinMac mac)
+		{
+			this.engine = new SkeinEngine(mac.engine);
+		}
+
+		public string AlgorithmName
+		{
+			get { return "Skein-MAC-" + (engine.BlockSize * 8) + "-" + (engine.OutputSize * 8); }
+		}
+
+		/// <summary>
+		/// Optionally initialises the Skein digest with the provided parameters.
+		/// </summary>
+		/// See <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"></see> for details on the parameterisation of the Skein hash function.
+		/// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param>
+		public void Init(ICipherParameters parameters)
+		{
+			SkeinParameters skeinParameters;
+			if (parameters is SkeinParameters)
+			{
+				skeinParameters = (SkeinParameters)parameters;
+			}
+			else if (parameters is KeyParameter)
+			{
+				skeinParameters = new SkeinParameters.Builder().SetKey(((KeyParameter)parameters).GetKey()).Build();
+			}
+			else
+			{
+				throw new ArgumentException("Invalid parameter passed to Skein MAC init - "
+				                            + parameters.GetType().Name);
+			}
+			if (skeinParameters.GetKey() == null)
+			{
+				throw new ArgumentException("Skein MAC requires a key parameter.");
+			}
+			engine.Init(skeinParameters);
+		}
+
+		public int GetMacSize()
+		{
+			return engine.OutputSize;
+		}
+
+		public void Reset()
+		{
+			engine.Reset();
+		}
+
+		public void Update(byte inByte)
+		{
+			engine.Update(inByte);
+		}
+
+		public void BlockUpdate(byte[] input, int inOff, int len)
+		{
+			engine.Update(input, inOff, len);
+		}
+
+		public int DoFinal(byte[] output, int outOff)
+		{
+			return engine.DoFinal(output, outOff);
+		}
+
+	}
+}
diff --git a/crypto/src/crypto/parameters/SkeinParameters.cs b/crypto/src/crypto/parameters/SkeinParameters.cs
new file mode 100644
index 000000000..bbd25e0e0
--- /dev/null
+++ b/crypto/src/crypto/parameters/SkeinParameters.cs
@@ -0,0 +1,285 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+
+	/// <summary>
+	/// Parameters for the Skein hash function - a series of byte[] strings identified by integer tags.
+	/// </summary>
+	/// <remarks>
+	/// Parameterised Skein can be used for:
+	/// <ul> 
+	/// <li>MAC generation, by providing a <see cref="SkeinParameters.Builder.SetKey(byte[])">key</see>.</li>
+	/// <li>Randomised hashing, by providing a <see cref="SkeinParameters.Builder.SetNoce(byte[])">nonce</see>.</li>
+	/// <li>A hash function for digital signatures, associating a
+	/// <see cref="SkeinParameters.Builder.SetPublicKey(byte[])">public key</see> with the message digest.</li>
+	/// <li>A key derivation function, by providing a
+	/// <see cref="SkeinParameters.Builder.SetKeyIdentifier(byte[])">key identifier</see>.</li>
+	/// <li>Personalised hashing, by providing a
+	/// <see cref="SkeinParameters.Builder.SetPersonalisation(DateTime,string,string)">recommended format</see> or
+	/// <see cref="SkeinParameters.Builder.SetPersonalisation(byte[])">arbitrary</see> personalisation string.</li>
+	/// </ul>
+	/// </remarks>
+	/// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinEngine"/>
+	/// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinDigest"/>
+	/// <seealso cref="Org.BouncyCastle.Crypto.Macs.SkeinMac"/>
+	public class SkeinParameters
+		: ICipherParameters
+	{
+		/// <summary>
+		/// The parameter type for a secret key, supporting MAC or KDF functions: 0
+		/// </summary>
+		public const int PARAM_TYPE_KEY = 0;
+
+		/// <summary>
+		/// The parameter type for the Skein configuration block: 4
+		/// </summary>
+		public const int PARAM_TYPE_CONFIG = 4;
+
+		/// <summary>
+		/// The parameter type for a personalisation string: 8
+		/// </summary>
+		public const int PARAM_TYPE_PERSONALISATION = 8;
+
+		/// <summary>
+		/// The parameter type for a public key: 12
+		/// </summary>
+		public const int PARAM_TYPE_PUBLIC_KEY = 12;
+
+		/// <summary>
+		/// The parameter type for a key identifier string: 16
+		/// </summary>
+		public const int PARAM_TYPE_KEY_IDENTIFIER = 16;
+
+		/// <summary>
+		/// The parameter type for a nonce: 20
+		/// </summary>
+		public const int PARAM_TYPE_NONCE = 20;
+
+		/// <summary>
+		/// The parameter type for the message: 48
+		/// </summary>
+		public const int PARAM_TYPE_MESSAGE = 48;
+
+		/// <summary>
+		/// The parameter type for the output transformation: 63
+		/// </summary>
+		public const int PARAM_TYPE_OUTPUT = 63;
+
+		private IDictionary parameters;
+
+		public SkeinParameters()
+			: this(Platform.CreateHashtable())
+
+		{
+		}
+
+		private SkeinParameters(IDictionary parameters)
+		{
+			this.parameters = parameters;
+		}
+
+		/// <summary>
+		/// Obtains a map of type (int) to value (byte[]) for the parameters tracked in this object.
+		/// </summary>
+		public IDictionary GetParameters()
+		{
+			return parameters;
+		}
+
+		/// <summary>
+		/// Obtains the value of the <see cref="PARAM_TYPE_KEY">key parameter</see>, or <code>null</code> if not
+		/// set.
+		/// </summary>
+		/// <returns>The key.</returns>
+		public byte[] GetKey()
+		{
+			return (byte[])parameters[PARAM_TYPE_KEY];
+		}
+
+		/// <summary>
+		/// Obtains the value of the <see cref="PARAM_TYPE_PERSONALISATION">personalisation parameter</see>, or
+		/// <code>null</code> if not set.
+		/// </summary>
+		public byte[] GetPersonalisation()
+		{
+			return (byte[])parameters[PARAM_TYPE_PERSONALISATION];
+		}
+
+		/// <summary>
+		/// Obtains the value of the <see cref="PARAM_TYPE_PUBLIC_KEY">public key parameter</see>, or
+		/// <code>null</code> if not set.
+		/// </summary>
+		public byte[] GetPublicKey()
+		{
+			return (byte[])parameters[PARAM_TYPE_PUBLIC_KEY];
+		}
+
+		/// <summary>
+		/// Obtains the value of the <see cref="PARAM_TYPE_KEY_IDENTIFIER key identifier parameter</see>, or
+		/// <code>null</code> if not set.
+		/// </summary>
+		public byte[] GetKeyIdentifier()
+		{
+			return (byte[])parameters[PARAM_TYPE_KEY_IDENTIFIER];
+		}
+
+		/// <summary>
+		/// Obtains the value of the <see cref="PARAM_TYPE_NONCE nonce parameter</see>, or <code>null</code> if
+		/// not set.
+		/// </summary>
+		public byte[] GetNonce()
+		{
+			return (byte[])parameters[PARAM_TYPE_NONCE];
+		}
+
+		/// <summary>
+		/// A builder for <see cref="SkeinParameters"/>.
+		/// </summary>
+		public class Builder
+		{
+			private IDictionary parameters = Platform.CreateHashtable();
+
+			public Builder()
+			{
+			}
+
+			public Builder(IDictionary paramsMap)
+			{
+				IEnumerator keys = paramsMap.Keys.GetEnumerator();
+				while (keys.MoveNext())
+				{
+					int key = (int)keys.Current;
+					parameters.Add(key, paramsMap[key]);
+				}
+			}
+
+			public Builder(SkeinParameters parameters)
+			{
+				IEnumerator keys = parameters.parameters.Keys.GetEnumerator();
+				while (keys.MoveNext())
+				{
+					int key = (int)keys.Current;
+					this.parameters.Add(key, parameters.parameters[key]);
+				}
+			}
+
+			/// <summary>
+			/// Sets a parameters to apply to the Skein hash function.
+			/// </summary>
+			/// <remarks>
+			/// Parameter types must be in the range 0,5..62, and cannot use the value 48
+			/// (reserved for message body).
+			/// <p/>
+			/// Parameters with type &lt; 48 are processed before
+			/// the message content, parameters with type &gt; 48
+			/// are processed after the message and prior to output.
+			/// </remarks>
+			/// <param name="type">the type of the parameter, in the range 5..62.</param>
+			/// <param name="value">the byte sequence of the parameter.</param>
+			public Builder Set(int type, byte[] value)
+			{
+				if (value == null)
+				{
+					throw new ArgumentException("Parameter value must not be null.");
+				}
+				if ((type != PARAM_TYPE_KEY)
+				    && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE))
+				{
+					throw new ArgumentException("Parameter types must be in the range 0,5..47,49..62.");
+				}
+				if (type == PARAM_TYPE_CONFIG)
+				{
+					throw new ArgumentException("Parameter type " + PARAM_TYPE_CONFIG
+					                            + " is reserved for internal use.");
+				}
+				this.parameters.Add(type, value);
+				return this;
+			}
+
+			/// <summary>
+			/// Sets the <see cref="SkeinParameters.PARAM_TYPE_KEY"/> parameter.
+			/// </summary>
+			public Builder SetKey(byte[] key)
+			{
+				return Set(PARAM_TYPE_KEY, key);
+			}
+
+			/// <summary>
+			/// Sets the <see cref="SkeinParameters.PARAM_TYPE_PERSONALISATION"/> parameter.
+			/// </summary>
+			public Builder SetPersonalisation(byte[] personalisation)
+			{
+				return Set(PARAM_TYPE_PERSONALISATION, personalisation);
+			}
+
+			/// <summary>
+			/// Implements the recommended personalisation format for Skein defined in Section 4.11 of
+			/// the Skein 1.3 specification.
+			/// </summary>
+			/// <remarks>
+			/// The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte
+			/// sequence using UTF-8 encoding.
+			/// </remarks>
+			/// <param name="date">the date the personalised application of the Skein was defined.</param>
+			/// <param name="emailAddress">the email address of the creation of the personalised application.</param>
+			/// <param name="distinguisher">an arbitrary personalisation string distinguishing the application.</param>
+			public Builder SetPersonalisation(DateTime date, string emailAddress, string distinguisher)
+			{
+				try
+				{
+					MemoryStream bout = new MemoryStream();
+					StreamWriter outBytes = new StreamWriter(bout, System.Text.Encoding.UTF8);
+					outBytes.Write(date.ToString("YYYYMMDD"));
+					outBytes.Write(" ");
+					outBytes.Write(emailAddress);
+					outBytes.Write(" ");
+					outBytes.Write(distinguisher);
+					outBytes.Close();
+					return Set(PARAM_TYPE_PERSONALISATION, bout.ToArray());
+				}
+				catch (IOException e)
+				{
+					throw new InvalidOperationException("Byte I/O failed.", e);
+				}
+			}
+
+			/// <summary>
+			/// Sets the <see cref="SkeinParameters.PARAM_TYPE_KEY_IDENTIFIER"/> parameter.
+			/// </summary>
+			public Builder SetPublicKey(byte[] publicKey)
+			{
+				return Set(PARAM_TYPE_PUBLIC_KEY, publicKey);
+			}
+
+			/// <summary>
+			/// Sets the <see cref="SkeinParameters.PARAM_TYPE_KEY_IDENTIFIER"/> parameter.
+			/// </summary>
+			public Builder SetKeyIdentifier(byte[] keyIdentifier)
+			{
+				return Set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier);
+			}
+
+			/// <summary>
+			/// Sets the <see cref="SkeinParameters.PARAM_TYPE_NONCE"/> parameter.
+			/// </summary>
+			public Builder SetNonce(byte[] nonce)
+			{
+				return Set(PARAM_TYPE_NONCE, nonce);
+			}
+
+			/// <summary>
+			/// Constructs a new <see cref="SkeinParameters"/> instance with the parameters provided to this
+			/// builder.
+			/// </summary>
+			public SkeinParameters Build()
+			{
+				return new SkeinParameters(parameters);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index 3c083034e..7d211e5dc 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -206,12 +206,52 @@ namespace Org.BouncyCastle.Utilities
 			return data == null ? null : (byte[]) data.Clone();
 		}
 
+		public static byte[] Clone(
+			byte[] data, 
+			byte[] existing)
+		{
+			if (data == null)
+			{
+				return null;
+			}
+			if ((existing == null) || (existing.Length != data.Length))
+			{
+				return Clone(data);
+			}
+			Array.Copy(data, 0, existing, 0, existing.Length);
+			return existing;
+		}
+
 		public static int[] Clone(
 			int[] data)
 		{
 			return data == null ? null : (int[]) data.Clone();
 		}
 
+		[CLSCompliantAttribute(false)]
+		public static ulong[] Clone(
+			ulong[] data)
+		{
+			return data == null ? null : (ulong[]) data.Clone();
+		}
+
+		[CLSCompliantAttribute(false)]
+		public static ulong[] Clone(
+			ulong[] data, 
+			ulong[] existing)
+		{
+			if (data == null)
+			{
+				return null;
+			}
+			if ((existing == null) || (existing.Length != data.Length))
+			{
+				return Clone(data);
+			}
+			Array.Copy(data, 0, existing, 0, existing.Length);
+			return existing;
+		}
+
 		public static void Fill(
 			byte[]	buf,
 			byte	b)
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index 0f08d47f4..576ae5ee4 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -42,6 +42,8 @@ namespace Org.BouncyCastle.Crypto.Tests
             new Threefish256Test(),
             new Threefish512Test(),
             new Threefish1024Test(),
+            new SkeinDigestTest(),
+            new SkeinMacTest(),
             new Cast5Test(),
             new Cast6Test(),
             new Gost28147Test(),
diff --git a/crypto/test/src/crypto/test/SkeinDigestTest.cs b/crypto/test/src/crypto/test/SkeinDigestTest.cs
new file mode 100644
index 000000000..b6f1c542b
--- /dev/null
+++ b/crypto/test/src/crypto/test/SkeinDigestTest.cs
@@ -0,0 +1,303 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+
+	[TestFixture]
+	public class SkeinDigestTest
+		: SimpleTest
+	{
+		private class Case
+		{
+			private byte[] message;
+			private byte[] digest;
+			private int blockSize;
+			private int outputSize;
+
+			public Case(int blockSize, int outputSize, string message, string digest)
+			{
+				this.blockSize = blockSize;
+				this.outputSize = outputSize;
+				this.message = Hex.Decode(message);
+				this.digest = Hex.Decode(digest);
+			}
+
+			public int getOutputSize()
+			{
+				return outputSize;
+			}
+
+			public int getBlockSize()
+			{
+				return blockSize;
+			}
+
+			public byte[] getMessage()
+			{
+				return message;
+			}
+
+			public byte[] getDigest()
+			{
+				return digest;
+			}
+
+		}
+
+		// Test cases from skein_golden_kat.txt and skein_golden_kat_short.txt in Skein 1.3 NIST CD
+		private static readonly Case[] TEST_CASES = {
+			new Case(256, 256, "", "c8877087da56e072870daa843f176e9453115929094c3a40c463a196c29bf7ba"),
+			new Case(256, 256, "fb", "088eb23cc2bccfb8171aa64e966d4af937325167dfcd170700ffd21f8a4cbdac"),
+			new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+			         "5c3002ff57a627089ea2f97a5000d5678416389019e80e45a3bbcab118315d26"),
+			new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a129233",
+			         "640c894a4bba6574c83e920ddf7dd2982fc634881bbbcb9d774eae0a285e89ce"),
+			new Case(256, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "0cd491b7715704c3a15a45a1ca8d93f8f646d3a1"),
+			new Case(256, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "afd1e2d0f5b6cd4e1f8b3935fa2497d27ee97e72060adac099543487"),
+			new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "4de6fe2bfdaa3717a4261030ef0e044ced9225d066354610842a24a3eafd1dcf"),
+			new Case(256, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "954620fb31e8b782a2794c6542827026fe069d715df04261629fcbe81d7d529b"
+			         + "95ba021fa4239fb00afaa75f5fd8e78b"),
+			new Case(256, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "51347e27c7eabba514959f899a6715ef6ad5cf01c23170590e6a8af399470bf9"
+			         + "0ea7409960a708c1dbaa90e86389df254abc763639bb8cdf7fb663b29d9557c3"),
+			new Case(256, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "6c9b6facbaf116b538aa655e0be0168084aa9f1be445f7e06714585e5999a6c9"
+			         + "84fffa9d41a316028692d4aad18f573fbf27cf78e84de26da1928382b023987d"
+			         + "cfe002b6201ea33713c54a8a5d9eb346f0365e04330d2faaf7bc8aba92a5d7fb"
+			         + "6345c6fb26750bce65ab2045c233627679ac6e9acb33602e26fe3526063ecc8b"),
+
+			new Case(512, 512, "", "bc5b4c50925519c290cc634277ae3d6257212395cba733bbad37a4af0fa06af4"
+			         + "1fca7903d06564fea7a2d3730dbdb80c1f85562dfcc070334ea4d1d9e72cba7a"),
+			new Case(512, 512, "fb", "c49e03d50b4b2cc46bd3b7ef7014c8a45b016399fd1714467b7596c86de98240"
+			         + "e35bf7f9772b7d65465cd4cffab14e6bc154c54fc67b8bc340abf08eff572b9e"),
+			new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+			         "abefb179d52f68f86941acbbe014cc67ec66ad78b7ba9508eb1400ee2cbdb06f"
+			         + "9fe7c2a260a0272d0d80e8ef5e8737c0c6a5f1c02ceb00fb2746f664b85fcef5"),
+			new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a129233",
+			         "5c5b7956f9d973c0989aa40a71aa9c48a65af2757590e9a758343c7e23ea2df4"
+			         + "057ce0b49f9514987feff97f648e1dd065926e2c371a0211ca977c213f14149f"),
+			new Case(512, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "ef03079d61b57c6047e15fa2b35b46fa24279539"),
+			new Case(512, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "d9e3219b214e15246a2038f76a573e018ef69b385b3bd0576b558231"),
+			new Case(512, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "809dd3f763a11af90912bbb92bc0d94361cbadab10142992000c88b4ceb88648"),
+			new Case(512, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "825f5cbd5da8807a7b4d3e7bd9cd089ca3a256bcc064cd73a9355bf3ae67f2bf"
+			         + "93ac7074b3b19907a0665ba3a878b262"),
+			new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "1a0d5abf4432e7c612d658f8dcfa35b0d1ab68b8d6bd4dd115c23cc57b5c5bcd"
+			         + "de9bff0ece4208596e499f211bc07594d0cb6f3c12b0e110174b2a9b4b2cb6a9"),
+
+			new Case(1024, 1024, "", "0fff9563bb3279289227ac77d319b6fff8d7e9f09da1247b72a0a265cd6d2a62"
+			         + "645ad547ed8193db48cff847c06494a03f55666d3b47eb4c20456c9373c86297"
+			         + "d630d5578ebd34cb40991578f9f52b18003efa35d3da6553ff35db91b81ab890"
+			         + "bec1b189b7f52cb2a783ebb7d823d725b0b4a71f6824e88f68f982eefc6d19c6"),
+			new Case(1024, 1024, "fb", "6426bdc57b2771a6ef1b0dd39f8096a9a07554565743ac3de851d28258fcff22"
+			         + "9993e11c4e6bebc8b6ecb0ad1b140276081aa390ec3875960336119427827473"
+			         + "4770671b79f076771e2cfdaaf5adc9b10cbae43d8e6cd2b1c1f5d6c82dc96618"
+			         + "00ddc476f25865b8748253173187d81da971c027d91d32fb390301c2110d2db2"),
+			new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+			         "140e93726ab0b0467c0b8a834ad8cda4d1769d273661902b70db0dcb5ee692ac"
+			         + "b3f852d03b11f857850f2428432811309c1dcbe5724f00267ea3667e89fadb4e"
+			         + "4911da6b0ba8a7eddf87c1c67152ef0f07b7fead3557318478bdef5ad1e5926d"
+			         + "7071fdd4bfa5076d4b3253f8de479ebdf5357676f1641b2f097e9b785e9e528e"),
+			new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a129233",
+			         "31105e1ef042c30b95b16e0f6e6a1a19172bb7d54a0597dd0c711194888efe1d"
+			         + "bce82d47416df9577ca387219f06e45cd10964ff36f6711edbbea0e9595b0f66"
+			         + "f72b755d70a46857e0aec98561a743d49370d8e572e212811273125f66cc30bf"
+			         + "117d3221894c48012bf6e2219de91e064b01523517420a1e00f71c4cc04bab62"),
+			new Case(1024, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "2e6a4cbf2ef05ea9c24b93e8d1de732ddf2739eb"),
+			new Case(1024, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "1d6de19f37f7a3c265440eecb4b9fbd3300bb5ac60895cfc0d4d3c72"),
+			new Case(1024, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "986a4d472b123e8148731a8eac9db23325f0058c4ccbc44a5bb6fe3a8db672d7"),
+			new Case(1024, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "9c3d0648c11f31c18395d5e6c8ebd73f43d189843fc45235e2c35e345e12d62b"
+			         + "c21a41f65896ddc6a04969654c2e2ce9"),
+			new Case(1024, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "5d0416f49c2d08dfd40a1446169dc6a1d516e23b8b853be4933513051de8d5c2"
+			         + "6baccffb08d3b16516ba3c6ccf3e9a6c78fff6ef955f2dbc56e1459a7cdba9a5"),
+			new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+			         + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+			         + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+			         + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+			         "96ca81f586c825d0360aef5acaec49ad55289e1797072eee198b64f349ce65b6"
+			         + "e6ed804fe38f05135fe769cc56240ddda5098f620865ce4a4278c77fa2ec6bc3"
+			         + "1c0f354ca78c7ca81665bfcc5dc54258c3b8310ed421d9157f36c093814d9b25"
+			         + "103d83e0ddd89c52d0050e13a64c6140e6388431961685734b1f138fe2243086"),
+
+		};
+
+		public override string Name
+		{
+			get { return "SkeinDigest"; }
+		}
+
+		public override void PerformTest()
+		{
+			for (int i = 0; i < TEST_CASES.Length; i++)
+			{
+				Case test = TEST_CASES[i];
+				runTest(test);
+			}
+		}
+
+		private void runTest(Case dc)
+		{
+			SkeinDigest digest = new SkeinDigest(dc.getBlockSize(), dc.getOutputSize());
+
+			byte[] message = dc.getMessage();
+			digest.BlockUpdate(message, 0, message.Length);
+
+			byte[] output = new byte[digest.GetDigestSize()];
+			digest.DoFinal(output, 0);
+
+			if (!AreEqual(output, dc.getDigest()))
+			{
+				Fail(digest.AlgorithmName + " message mismatch.\n Message " + Hex.ToHexString(dc.getMessage()),
+				     Hex.ToHexString(dc.getDigest()), Hex.ToHexString(output));
+			}
+
+			// Clone test
+			digest.BlockUpdate(message, 0, message.Length / 2);
+
+			// clone the Digest
+			IDigest d = new SkeinDigest(digest);
+
+			digest.BlockUpdate(message, message.Length / 2, message.Length - message.Length / 2);
+			digest.DoFinal(output, 0);
+
+			if (!AreEqual(dc.getDigest(), output))
+			{
+				Fail("failing clone vector test", Hex.ToHexString(dc.getDigest()), Hex.ToHexString(output));
+			}
+
+			d.BlockUpdate(message, message.Length / 2, message.Length - message.Length / 2);
+			d.DoFinal(output, 0);
+
+			if (!AreEqual(dc.getDigest(), output))
+			{
+				Fail("failing second clone vector test", Hex.ToHexString(dc.getDigest()), Hex.ToHexString(output));
+			}
+
+			//	        //
+			//	        // memo test
+			//	        //
+			//	        Memoable m = (Memoable)digest;
+			//
+			//	        digest.Update(message, 0, message.Length / 2);
+			//
+			//	        // copy the Digest
+			//	        Memoable copy1 = m.copy();
+			//	        Memoable copy2 = copy1.copy();
+			//
+			//	        digest.Update(message, message.Length / 2, message.Length - message.Length / 2);
+			//	        digest.DoFinal(output, 0);
+			//
+			//	        if (!AreEqual(dc.getDigest(), output))
+			//	        {
+			//				Fail("failing memo vector test", Hex.ToHexString(dc.getDigest()), Hex.ToHexString(output));
+			//	        }
+			//
+			//	        m.reset(copy1);
+			//
+			//	        digest.Update(message, message.Length / 2, message.Length - message.Length / 2);
+			//	        digest.DoFinal(output, 0);
+			//
+			//	        if (!AreEqual(dc.getDigest(), output))
+			//	        {
+			//				fail("failing memo reset vector test", Hex.ToHexString(dc.getDigest()), Hex.ToHexString(output));
+			//	        }
+			//
+			//	        IDigest md = (IDigest)copy2;
+			//
+			//	        md.Update(message, message.Length / 2, message.Length - message.Length / 2);
+			//	        md.DoFinal(output, 0);
+			//
+			//	        if (!AreEqual(dc.getDigest(), output))
+			//	        {
+			//				Fail("failing memo copy vector test", Hex.ToHexString(dc.getDigest()), Hex.ToHexString(output));
+			//	        }
+		}
+
+		public static void Main(
+			string[] args)
+		{
+			RunTest(new SkeinDigestTest());
+		}
+
+		[Test]
+		public void TestFunction()
+		{
+			string resultText = Perform().ToString();
+
+			Assert.AreEqual(Name + ": Okay", resultText);
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/crypto/test/src/crypto/test/SkeinMacTest.cs b/crypto/test/src/crypto/test/SkeinMacTest.cs
new file mode 100644
index 000000000..852c3b2c7
--- /dev/null
+++ b/crypto/test/src/crypto/test/SkeinMacTest.cs
@@ -0,0 +1,174 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+
+	[TestFixture]
+	public class SkeinMacTest
+		: SimpleTest
+	{
+		private class Case
+		{
+			private byte[] message;
+			private byte[] digest;
+			private byte[] key;
+			private int blockSize;
+			private int outputSize;
+
+			public Case(int blockSize, int outputSize, String message, String key, String digest)
+			{
+				this.blockSize = blockSize;
+				this.outputSize = outputSize;
+				this.message = Hex.Decode(message);
+				this.key = Hex.Decode(key);
+				this.digest = Hex.Decode(digest);
+			}
+
+			public int getOutputSize()
+			{
+				return outputSize;
+			}
+
+			public int getBlockSize()
+			{
+				return blockSize;
+			}
+
+			public byte[] getMessage()
+			{
+				return message;
+			}
+
+			public byte[] getKey()
+			{
+				return key;
+			}
+
+			public byte[] getDigest()
+			{
+				return digest;
+			}
+
+			public override string ToString()
+			{
+				return String.Format("new Case({0}, {1}, \"{2}\", \"{3}\", \"{4}\"),", blockSize, outputSize,
+				                     Hex.ToHexString(message), Hex.ToHexString(key), Hex.ToHexString(digest));
+			}
+
+		}
+
+		// Test cases from skein_golden_kat.txt in Skein 1.3 NIST CD
+		// Excludes empty '(none)' key 'random+MAC' tests, which are in effect digest 
+		private static readonly Case[] TEST_CASES = {
+			new Case(256, 256, "", "cb41f1706cde09651203c2d0efbaddf8", "886e4efefc15f06aa298963971d7a25398fffe5681c84db39bd00851f64ae29d"),
+			new Case(256, 256, "d3", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "979422a94e3afaa46664124d4e5e8b9422b1d8baf11c6ae6725992ac72a112ca"),
+			new Case(256, 256, "d3090c72", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "1d658372cbea2f9928493cc47599d6f4ad8ce33536bedfa20b739f07516519d5"),
+			new Case(256, 256, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "41ef6b0f0fad81c040284f3b1a91e9c44e4c26a6d7207f3aac4362856ef12aca"),
+			new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "ca8208119b9e4e4057631ab31015cfd256f6763a0a34381633d97f640899b84f"),
+			new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "9e9980fcc16ee082cf164a5147d0e0692aeffe3dcb8d620e2bb542091162e2e9"),
+			new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc235", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "c353a316558ec34f8245dd2f9c2c4961fbc7decc3b69053c103e4b8aaaf20394"),
+			new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf8", "b1b8c18188e69a6ecae0b6018e6b638c6a91e6de6881e32a60858468c17b520d"),
+			new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "1dfd2515a412e78852cd81a7f2167711b4ca19b2891c2ea36ba94f8451944793"),
+			new Case(256, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf8", "a097340709b443ed2c0a921f5dcefef3ead65c4f0bcd5f13da54d7ed"),
+			new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "ac1b4fab6561c92d0c487e082daec53e0db4f505e08bf51cae4fd5375e37fc04"),
+			new Case(256, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "96e6cebb23573d0a70ce36a67aa05d2403148093f25c695e1254887cc97f9771d2518413af4286bf2a06b61a53f7fcec"),
+			new Case(256, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "0e95e597e71d6350f20b99c4179f54f43a4722705c06ba765a82cb0a314fe2fe87ef8090063b757e53182706ed18737dadc0da1e1c66518f08334052702c5ed7"),
+			new Case(256, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf8", "064abd4896f460b1953f5a357e7f7c5256e29cdb62b8740d0b52295cfa2ef4c7a2"),
+			new Case(256, 520, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "edf220e43e048603bd16197d59b673b9974de5b8bcf7cb1558a4799f6fd3743eb5fb400cd6129afc0c60e7b741b7e5806f0e0b93eb8429fbc7efa222175a9c80fd"),
+			new Case(256, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "f3f59fb07399c7b73aae02a8590883cb2fdfde75c55654e71846522301bde48d267169adcc559e038e8c2f28faa552b550d51874055384adea93c036c71a1f0af0c7bcc3bc923738d5307b9da7cb423d4e615c629c4aba71f70d4c9d1fa008176825e51bfa0203445a4083947ec19f6a0fbd082b5b970f2396fb67420639410447"),
+			new Case(256, 2056, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "80eb80d9b8836b32fa576fc84ba08edfbdfd6979123d61914e610a70a372b37f560a10909484f9f4a377c93e29ba681dfe522c41dc83b5ee0567e5370007c7bbe4df0b2b4a25e088f80d72fc30734cdcd76d817b42fbd44dca881019afb25306f19d4e91848778af306517d2072cef72caa327e877c5b6554f83cec3d00877131b47c4d3b557f5a13541c4d5080ee3ce7a658993d083efd0db3496a8752060c3c8552f44b290cabdcc867f691ad605836c08dbd59c9528d885b600b85fdfc8a9d0e636ac3ad8b4295bcb0169e78dc358e77eacc8c4b61bddfa9e5f32d2268a006cfe05c57150fe8e68cabd21cf6cf6035aa1fe4db36c922b765aad0b64e82a2c37"),
+			new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed696e6c9db1e6abea026288954a9c2d5758d7c5db7c9e48aa3d21cae3d977a7c3926066aa393dbd538dd0c30da8916c8757f24c18488014668a2627163a37b261833dc2f8c3c56b1b2e0be21fd3fbdb507b2950b77a6cc02efb393e57419383a920767bca2c972107aa61384542d47cbfb82cfe5c415389d1b0a2d74e2c5da851", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "8f88de68f03cd2f396ccdd49c3a0f4ff15bcda7eb357da9753f6116b124de91d"),
+			new Case(512, 512, "", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "9bd43d2a2fcfa92becb9f69faab3936978f1b865b7e44338fc9c8f16aba949ba340291082834a1fc5aa81649e13d50cd98641a1d0883062bfe2c16d1faa7e3aa"),
+			new Case(512, 512, "d3", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "f0c0a10f031c8fc69cfabcd54154c318b5d6cd95d06b12cf20264402492211ee010d5cecc2dc37fd772afac0596b2bf71e6020ef2dee7c860628b6e643ed9ff6"),
+			new Case(512, 512, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "0c1f1921253dd8e5c2d4c5f4099f851042d91147892705829161f5fc64d89785226eb6e187068493ee4c78a4b7c0f55a8cbbb1a5982c2daf638fc6a74b16b0d7"),
+			new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "478d7b6c0cc6e35d9ebbdedf39128e5a36585db6222891692d1747d401de34ce3db6fcbab6c968b7f2620f4a844a2903b547775579993736d2493a75ff6752a1"),
+			new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e59", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "13c170bac1de35e5fb843f65fabecf214a54a6e0458a4ff6ea5df91915468f4efcd371effa8965a9e82c5388d84730490dcf3976af157b8baf550655a5a6ab78"),
+			new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc235", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "a947812529a72fd3b8967ec391b298bee891babc8487a1ec4ea3d88f6b2b5be09ac6a780f30f8e8c3bbb4f18bc302a28f3e87d170ba0f858a8fefe3487478cca"),
+			new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "7690ba61f10e0bba312980b0212e6a9a51b0e9aadfde7ca535754a706e042335b29172aae29d8bad18efaf92d43e6406f3098e253f41f2931eda5911dc740352"),
+			new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "d10e3ba81855ac087fbf5a3bc1f99b27d05f98ba22441138026225d34a418b93fd9e8dfaf5120757451adabe050d0eb59d271b0fe1bbf04badbcf9ba25a8791b"),
+			new Case(512, 160, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "5670b226156570dff3efe16661ab86eb24982cdf"),
+			new Case(512, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "c41b9ff9753e6c0f8ed88866e320535e927fe4da552c289841a920db"),
+			new Case(512, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "dfbf5c1319a1d9d70efb2f1600fbcf694f935907f31d24a16d6cd2fb2d7855a769681766c0a29da778eed346cd1d740f"),
+			new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "04d8cddb0ad931d54d195899a094684344e902286037272890bce98a41813edc37a3cee190a693fcca613ee30049ce7ec2bdff9613f56778a13f8c28a21d167a"),
+			new Case(512, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "08fca368b3b14ac406676adf37ac9be2dbb8704e694055a0c6331184d4f0070098f23f0963ee29002495771bf56fb4d3d9ff3506abcd80be927379f7880d5d7703919fbf92184f498ac44f47f015ce676eded9165d47d53733f5a27abbc05f45acd98b97cc15ffdced641defd1a5119ef841b452a1b8f94ee69004466ccdc143"),
+			new Case(512, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "669e770ebe7eacc2b64caaf049923ad297a5b37cfa61c283392d81ccfcb9bbbc09"),
+			new Case(512, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "acc2e03f07f33e9820a6038421089429adcd6a7a83f733beec048c05bf37531a170a5537fcb565c348a70a83217f8be768ff6f95fd2b3d89cb7d8a3dc849505e3710eb4e65a8e7134bbf580d92fe18c9aa987563669b1f014aa5e092519089355534eaa9f0bdc99f6839f54080ffe74623254c906ecb8896b4346c3178a0bc2898"),
+			new Case(512, 2056, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "9f3e082223c43090a4a3ffbdcd469cbabfe0c1399d1edf45a5dfc18f4db5428928a76e979b8d0d5dffec0e6a59ada448c1ffbc06cc80a2006f002adc0c6dbf458563762228dce4381944e460ebebfe06f1237093634625107469a22a189a47f8b025899265d8890a1b39df64552394377e88ba2ad44a8c8d174f884ac8c3ae24ddb0affca5fceb6aa76e09706881e8371774b9b050a69b96ef5e97e81043f8b7e9479e287ab441bacd62caf768a82c8c3e3107be70eb8799a39856fe29842a04e25de0ef9de1b7e65bd0f1f7306835287fc957388e2035b7d22d3aa9c06a9fefbca16f3f60e1c4def89038d918942152a069aa2e0be8ae7475d859031adec84583"),
+			new Case(1024, 1024, "", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "bcf37b3459c88959d6b6b58b2bfe142cef60c6f4ec56b0702480d7893a2b0595aa354e87102a788b61996b9cbc1eade7dafbf6581135572c09666d844c90f066b800fc4f5fd1737644894ef7d588afc5c38f5d920bdbd3b738aea3a3267d161ed65284d1f57da73b68817e17e381ca169115152b869c66b812bb9a84275303f0"),
+			new Case(1024, 1024, "d3090c72", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "df0596e5808835a3e304aa27923db05f61dac57c0696a1d19abf188e70aa9dbcc659e9510f7c9a37fbc025bd4e5ea293e78ed7838dd0b08864e8ad40ddb3a88031ebefc21572a89960d1916107a7da7ac0c067e34ec46a86a29ca63fa250bd398eb32ec1ed0f8ac8329f26da018b029e41e2e58d1dfc44de81615e6c987ed9c9"),
+			new Case(1024, 1024, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "3cfbb79cd88af8ee09c7670bcbab6907a31f80fa31d9d7c9d50826c9568f307a78bd254961398c76b6e338fd9ca5f351059350d30963c3320659b223b991fc46d1307686fe2b4763d9f593c57ad5adbc45caf2ea3dc6090f5a74fa5fa6d9e9838964ea0a2aa216831ab069b00629a1a9b037083403bdb25d3d06a21c430c87dd"),
+			new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e59", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "0a1b960099fc9d653b0fd1f5b6b972fb366907b772cbce5a59b6171d7935506f70c212bd169d68c5cfd8618343611b7eb2e686ff1dc7c03a57e1a55ed10726848161eea903d53b58459be42d95df989c66c2eea4e51cde272c2d8be67bf3bca2aee633777eb8486781eaa060d0f538abd6c93dbd2d1bf66e6f50bfdcac3725a4"),
+			new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "3e0cd7938d71c39ffbb08a6ba7995ade3ad140e2c0c45cdbafb099247e08e4c20b61c1f885ced5ed2f816680925034918236e5807f0eecf3f27e9cfca36675eb75873efa1fb41f17541dc2f7c2469eaecb35cc7ca58e489804caf56f09fb97c9f689c64ad49c6888f86c483e901bd3d25798b394ef93faf9154900f92f31f433"),
+			new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "7266752f7e9aa04bd7d8a1b16030677de6021301f6a62473c76bae2b98bbf8aad73bd00a4b5035f741caf2317ab80e4e97f5c5bbe8acc0e8b424bcb13c7c6740a985801fba54addde8d4f13f69d2bfc98ae104d46a211145217e51d510ea846cec9581d14fda079f775c8b18d66cb31bf7060996ee8a69eee7f107909ce59a97"),
+			new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "71f40bf2aa635125ef83c8df0d4e9ea18b73b56be4f45e89b910a7c68d396b65b09d18abc7d1b6de3f53fd5de583e6f22e612dd17b292068af6027daaf8b4cd60acf5bc85044741e9f7a1f423f5827f5e360930a2e71912239af9fc6343604fdcf3f3569854f2bb8d25a81e3b3f5261a02fe8292aaaa50c324101ab2c7a2f349"),
+			new Case(1024, 160, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "17c3c533b27d666da556ae586e641b7a3a0bcc45"),
+			new Case(1024, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "6625df9801581009125ea4e5c94ad6f1a2d692c278822ccb6eb67235"),
+			new Case(1024, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "6c5b671c1766f6eecea6d24b641d4a6bf84bba13a1976f8f80b3f30ee2f93de6"),
+			new Case(1024, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "98af454d7fa3706dfaafbf58c3f9944868b57f68f493987347a69fce19865febba0407a16b4e82065035651f0b1e0327"),
+			new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "211ac479e9961141da3aac19d320a1dbbbfad55d2dce87e6a345fcd58e36827597378432b482d89bad44dddb13e6ad86e0ee1e0882b4eb0cd6a181e9685e18dd302ebb3aa74502c06254dcadfb2bd45d288f82366b7afc3bc0f6b1a3c2e8f84d37fbedd07a3f8fcff84faf24c53c11da600aaa118e76cfdcb366d0b3f7729dce"),
+			new Case(1024, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "dc1d253b7cadbdaef18503b1809a7f1d4f8c323b7f6f8ca50b76d3864649ce1c7d"),
+			new Case(1024, 520, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "decd79578d12bf6806530c382230a2c7836429c70cac941179e1dd982938bab91fb6f3638df1cc1ef615ecfc4249e5aca8a73c4c1eebef662a836d0be903b00146"),
+			new Case(1024, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "440fe691e04f1fed8c253d6c4670646156f33fffaea702de9445df5739eb960cecf85d56e2e6860a610211a5c909932ab774b978aa0b0d5bbce82775172ab12dceddd51d1eb030057ce61bea6c18f6bb368d26ae76a9e44a962eb132e6c42c25d9fecc4f13348300ca55c78e0990de96c1ae24eb3ee3324782c93dd628260a2c8d"),
+			new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed696e6c9db1e6abea026288954a9c2d5758d7c5db7c9e48aa3d21cae3d977a7c3926066aa393dbd538dd0c30da8916c8757f24c18488014668a2627163a37b261833dc2f8c3c56b1b2e0be21fd3fbdb507b2950b77a6cc02efb393e57419383a920767bca2c972107aa61384542d47cbfb82cfe5c415389d1b0a2d74e2c5da851", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "46a42b0d7b8679f8fcea156c072cf9833c468a7d59ac5e5d326957d60dfe1cdfb27eb54c760b9e049fda47f0b847ac68d6b340c02c39d4a18c1bdfece3f405fae8aa848bdbefe3a4c277a095e921228618d3be8bd1999a071682810de748440ad416a97742cc9e8a9b85455b1d76472cf562f525116698d5cd0a35ddf86e7f8a"),
+
+		};
+
+		public override string Name
+		{
+			get { return "SkeinMac"; }
+		}
+
+		public override void PerformTest()
+		{
+			for (int i = 0; i < TEST_CASES.Length; i++)
+			{
+				Case test = TEST_CASES[i];
+				runTest(test);
+			}
+		}
+
+		private void runTest(Case dc)
+		{
+			IMac digest = new SkeinMac(dc.getBlockSize(), dc.getOutputSize());
+			digest.Init(new KeyParameter(dc.getKey()));
+
+			byte[] message = dc.getMessage();
+			digest.BlockUpdate(message, 0, message.Length);
+
+			byte[] output = new byte[digest.GetMacSize()];
+			digest.DoFinal(output, 0);
+
+			if (!AreEqual(output, dc.getDigest()))
+			{
+				Fail(digest.AlgorithmName + " message " + (dc.getMessage().Length * 8) + " mismatch.\n Message  " + Hex.ToHexString(dc.getMessage())
+				     + "\n Key      " + Hex.ToHexString(dc.getKey()) + "\n Expected "
+				     + Hex.ToHexString(dc.getDigest()) + "\n Actual   " + Hex.ToHexString(output));
+			}
+
+		}
+
+		public static void Main(
+			string[] args)
+		{
+			RunTest(new SkeinMacTest());
+		}
+
+		[Test]
+		public void TestFunction()
+		{
+			string resultText = Perform().ToString();
+
+			Assert.AreEqual(Name + ": Okay", resultText);
+		}
+
+	}
+}
\ No newline at end of file