summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2014-01-21 16:46:07 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2014-01-21 16:46:07 +0700
commit95c58fd065df852598412fa8b7d06c3cee72c29c (patch)
tree01d8a520a72b008656881a37ec88d0ab2c98c361 /crypto/src
parentMerge branch 'pkix-validator-throw' of git://github.com/jstedfast/bc-csharp i... (diff)
parentPort HMac optimisation using Memoable digests from bc-java. (diff)
downloadBouncyCastle.NET-ed25519-95c58fd065df852598412fa8b7d06c3cee72c29c.tar.xz
Merge branch 'feature/threefish-skein-memoable-sm3' of git://github.com/timw/bc-csharp into timw-feature/threefish-skein-memoable-sm3
Conflicts:
	crypto/crypto.mdp
	crypto/src/util/Arrays.cs
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/crypto/digests/GOST3411Digest.cs49
-rw-r--r--crypto/src/crypto/digests/GeneralDigest.cs15
-rw-r--r--crypto/src/crypto/digests/LongDigest.cs15
-rw-r--r--crypto/src/crypto/digests/MD2Digest.cs26
-rw-r--r--crypto/src/crypto/digests/MD4Digest.cs23
-rw-r--r--crypto/src/crypto/digests/MD5Digest.cs24
-rw-r--r--crypto/src/crypto/digests/RipeMD128Digest.cs26
-rw-r--r--crypto/src/crypto/digests/RipeMD160Digest.cs22
-rw-r--r--crypto/src/crypto/digests/RipeMD256Digest.cs21
-rw-r--r--crypto/src/crypto/digests/RipeMD320Digest.cs21
-rw-r--r--crypto/src/crypto/digests/SHA3Digest.cs21
-rw-r--r--crypto/src/crypto/digests/Sha1Digest.cs21
-rw-r--r--crypto/src/crypto/digests/Sha224Digest.cs21
-rw-r--r--crypto/src/crypto/digests/Sha256Digest.cs21
-rw-r--r--crypto/src/crypto/digests/Sha384Digest.cs14
-rw-r--r--crypto/src/crypto/digests/Sha512Digest.cs14
-rw-r--r--crypto/src/crypto/digests/Sha512tDigest.cs39
-rw-r--r--crypto/src/crypto/digests/SkeinDigest.cs117
-rw-r--r--crypto/src/crypto/digests/SkeinEngine.cs803
-rw-r--r--crypto/src/crypto/digests/Sm3Digest.cs328
-rw-r--r--crypto/src/crypto/digests/TigerDigest.cs41
-rw-r--r--crypto/src/crypto/digests/WhirlpoolDigest.cs44
-rw-r--r--crypto/src/crypto/engines/ThreefishEngine.cs1491
-rw-r--r--crypto/src/crypto/macs/HMac.cs44
-rw-r--r--crypto/src/crypto/macs/SkeinMac.cs117
-rw-r--r--crypto/src/crypto/parameters/SkeinParameters.cs285
-rw-r--r--crypto/src/crypto/parameters/TweakableBlockCipherParameters.cs40
-rw-r--r--crypto/src/util/Arrays.cs24
-rw-r--r--crypto/src/util/IMemoable.cs29
-rw-r--r--crypto/src/util/MemoableResetException.cs27
30 files changed, 3708 insertions, 75 deletions
diff --git a/crypto/src/crypto/digests/GOST3411Digest.cs b/crypto/src/crypto/digests/GOST3411Digest.cs
index 9f0bec9e6..218adf68c 100644
--- a/crypto/src/crypto/digests/GOST3411Digest.cs
+++ b/crypto/src/crypto/digests/GOST3411Digest.cs
@@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 	* implementation of GOST R 34.11-94
 	*/
 	public class Gost3411Digest
-		: IDigest
+		: IDigest, IMemoable
 	{
 		private const int DIGEST_LENGTH = 32;
 
@@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 		private ulong	byteCount;
 
 		private readonly IBlockCipher cipher = new Gost28147Engine();
-		private readonly byte[] sBox;
+		private byte[] sBox;
 
 		private static byte[][] MakeC()
 		{
@@ -65,22 +65,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 		 */
 		public Gost3411Digest(Gost3411Digest t)
 		{
-			this.sBox = t.sBox;
-			cipher.Init(true, new ParametersWithSBox(null, sBox));
-
-			Reset();
-
-			Array.Copy(t.H, 0, this.H, 0, t.H.Length);
-			Array.Copy(t.L, 0, this.L, 0, t.L.Length);
-			Array.Copy(t.M, 0, this.M, 0, t.M.Length);
-			Array.Copy(t.Sum, 0, this.Sum, 0, t.Sum.Length);
-			Array.Copy(t.C[1], 0, this.C[1], 0, t.C[1].Length);
-			Array.Copy(t.C[2], 0, this.C[2], 0, t.C[2].Length);
-			Array.Copy(t.C[3], 0, this.C[3], 0, t.C[3].Length);
-			Array.Copy(t.xBuf, 0, this.xBuf, 0, t.xBuf.Length);
-
-			this.xBufOff = t.xBufOff;
-			this.byteCount = t.byteCount;
+			Reset(t);
 		}
 
 		public string AlgorithmName
@@ -339,5 +324,33 @@ namespace Org.BouncyCastle.Crypto.Digests
 		{
 			return 32;
 		}
+
+		public IMemoable Copy()
+		{
+			return new Gost3411Digest(this);
+		}
+
+		public void Reset(IMemoable other)
+		{
+			Gost3411Digest t = (Gost3411Digest)other;
+
+			this.sBox = t.sBox;
+			cipher.Init(true, new ParametersWithSBox(null, sBox));
+
+			Reset();
+
+			Array.Copy(t.H, 0, this.H, 0, t.H.Length);
+			Array.Copy(t.L, 0, this.L, 0, t.L.Length);
+			Array.Copy(t.M, 0, this.M, 0, t.M.Length);
+			Array.Copy(t.Sum, 0, this.Sum, 0, t.Sum.Length);
+			Array.Copy(t.C[1], 0, this.C[1], 0, t.C[1].Length);
+			Array.Copy(t.C[2], 0, this.C[2], 0, t.C[2].Length);
+			Array.Copy(t.C[3], 0, this.C[3], 0, t.C[3].Length);
+			Array.Copy(t.xBuf, 0, this.xBuf, 0, t.xBuf.Length);
+
+			this.xBufOff = t.xBufOff;
+			this.byteCount = t.byteCount;
+		}
 	}
+
 }
diff --git a/crypto/src/crypto/digests/GeneralDigest.cs b/crypto/src/crypto/digests/GeneralDigest.cs
index 77c17ed58..54a09ae05 100644
--- a/crypto/src/crypto/digests/GeneralDigest.cs
+++ b/crypto/src/crypto/digests/GeneralDigest.cs
@@ -1,5 +1,7 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Digests
 {
     /**
@@ -7,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Digests
     * "Handbook of Applied Cryptography", pages 344 - 347.
     */
     public abstract class GeneralDigest
-		: IDigest
+		: IDigest, IMemoable
     {
         private const int BYTE_LENGTH = 64;
 
@@ -22,8 +24,13 @@ namespace Org.BouncyCastle.Crypto.Digests
         }
 
         internal GeneralDigest(GeneralDigest t)
-        {
-            xBuf = new byte[t.xBuf.Length];
+		{
+			xBuf = new byte[t.xBuf.Length];
+			CopyIn(t);
+		}
+
+		protected void CopyIn(GeneralDigest t)
+		{
             Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length);
 
             xBufOff = t.xBufOff;
@@ -114,5 +121,7 @@ namespace Org.BouncyCastle.Crypto.Digests
         public abstract string AlgorithmName { get; }
 		public abstract int GetDigestSize();
         public abstract int DoFinal(byte[] output, int outOff);
+		public abstract IMemoable Copy();
+		public abstract void Reset(IMemoable t);
     }
 }
diff --git a/crypto/src/crypto/digests/LongDigest.cs b/crypto/src/crypto/digests/LongDigest.cs
index 702753b2b..9ee9bcd57 100644
--- a/crypto/src/crypto/digests/LongDigest.cs
+++ b/crypto/src/crypto/digests/LongDigest.cs
@@ -2,6 +2,7 @@ using System;
 
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -9,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Digests
     * Base class for SHA-384 and SHA-512.
     */
     public abstract class LongDigest
-		: IDigest
+		: IDigest, IMemoable
     {
         private int     MyByteLength = 128;
 
@@ -41,8 +42,14 @@ namespace Org.BouncyCastle.Crypto.Digests
         */
         internal LongDigest(
 			LongDigest t)
-        {
-            xBuf = new byte[t.xBuf.Length];
+		{
+			xBuf = new byte[t.xBuf.Length];
+
+			CopyIn(t);
+		}
+
+		protected void CopyIn(LongDigest t)
+		{
             Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length);
 
             xBufOff = t.xBufOff;
@@ -342,5 +349,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 		public abstract string AlgorithmName { get; }
 		public abstract int GetDigestSize();
         public abstract int DoFinal(byte[] output, int outOff);
+		public abstract IMemoable Copy();
+		public abstract void Reset(IMemoable t);
     }
 }
diff --git a/crypto/src/crypto/digests/MD2Digest.cs b/crypto/src/crypto/digests/MD2Digest.cs
index 78c696f33..6d90f3f9d 100644
--- a/crypto/src/crypto/digests/MD2Digest.cs
+++ b/crypto/src/crypto/digests/MD2Digest.cs
@@ -1,5 +1,7 @@
 using System;
+
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -9,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests
     * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992
     */
     public class MD2Digest
-		: IDigest
+		: IDigest, IMemoable
     {
         private const int DigestLength = 16;
         private const int BYTE_LENGTH = 16;
@@ -32,8 +34,14 @@ namespace Org.BouncyCastle.Crypto.Digests
         {
             Reset();
         }
+
         public MD2Digest(MD2Digest t)
-        {
+		{
+			CopyIn(t);
+		}
+
+		private void CopyIn(MD2Digest t)
+		{
             Array.Copy(t.X, 0, X, 0, t.X.Length);
             xOff = t.xOff;
             Array.Copy(t.M, 0, M, 0, t.M.Length);
@@ -41,6 +49,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             Array.Copy(t.C, 0, C, 0, t.C.Length);
             COff = t.COff;
         }
+
         /**
         * return the algorithm name
         *
@@ -242,6 +251,19 @@ namespace Org.BouncyCastle.Crypto.Digests
         (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51,
         (byte)159,(byte)17,(byte)131,(byte)20
         };
+
+		public IMemoable Copy()
+		{
+			return new MD2Digest(this);
+		}
+
+		public void Reset(IMemoable other)
+		{
+			MD2Digest d = (MD2Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 
 }
diff --git a/crypto/src/crypto/digests/MD4Digest.cs b/crypto/src/crypto/digests/MD4Digest.cs
index bc4eae0fd..8743f7dad 100644
--- a/crypto/src/crypto/digests/MD4Digest.cs
+++ b/crypto/src/crypto/digests/MD4Digest.cs
@@ -1,5 +1,7 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Digests
 {
     /**
@@ -32,7 +34,13 @@ namespace Org.BouncyCastle.Crypto.Digests
         * message digest.
         */
         public MD4Digest(MD4Digest t) : base(t)
-        {
+		{
+			CopyIn(t);
+		}
+
+		private void CopyIn(MD4Digest t)
+		{
+			base.CopyIn(t);
             H1 = t.H1;
             H2 = t.H2;
             H3 = t.H3;
@@ -266,6 +274,19 @@ namespace Org.BouncyCastle.Crypto.Digests
                 X[i] = 0;
             }
         }
+
+		public override IMemoable Copy()
+		{
+			return new MD4Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			MD4Digest d = (MD4Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 
 }
diff --git a/crypto/src/crypto/digests/MD5Digest.cs b/crypto/src/crypto/digests/MD5Digest.cs
index 2116d64f0..c60ac92a3 100644
--- a/crypto/src/crypto/digests/MD5Digest.cs
+++ b/crypto/src/crypto/digests/MD5Digest.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -28,7 +29,13 @@ namespace Org.BouncyCastle.Crypto.Digests
         */
         public MD5Digest(MD5Digest t)
             : base(t)
-        {
+		{
+			CopyIn(t);
+		}
+
+		private void CopyIn(MD5Digest t)
+		{
+			base.CopyIn(t);
             H1 = t.H1;
             H2 = t.H2;
             H3 = t.H3;
@@ -287,7 +294,20 @@ namespace Org.BouncyCastle.Crypto.Digests
 
             xOff = 0;
         }
-    }
+
+		public override IMemoable Copy()
+		{
+			return new MD5Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			MD5Digest d = (MD5Digest)other;
+
+			CopyIn(d);
+		}
+
+	}
 
 }
 
diff --git a/crypto/src/crypto/digests/RipeMD128Digest.cs b/crypto/src/crypto/digests/RipeMD128Digest.cs
index 8977583a4..e8a0331ca 100644
--- a/crypto/src/crypto/digests/RipeMD128Digest.cs
+++ b/crypto/src/crypto/digests/RipeMD128Digest.cs
@@ -1,5 +1,7 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Digests
 {
     /**
@@ -28,8 +30,15 @@ namespace Org.BouncyCastle.Crypto.Digests
         * message digest.
         */
         public RipeMD128Digest(RipeMD128Digest t) : base(t)
-        {
-            H0 = t.H0;
+		{
+			CopyIn(t);
+		}
+
+		private void CopyIn(RipeMD128Digest t)
+		{
+			base.CopyIn(t);
+
+			H0 = t.H0;
             H1 = t.H1;
             H2 = t.H2;
             H3 = t.H3;
@@ -457,6 +466,19 @@ namespace Org.BouncyCastle.Crypto.Digests
                 X[i] = 0;
             }
         }
+		
+		public override IMemoable Copy()
+		{
+			return new RipeMD128Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			RipeMD128Digest d = (RipeMD128Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 
 }
diff --git a/crypto/src/crypto/digests/RipeMD160Digest.cs b/crypto/src/crypto/digests/RipeMD160Digest.cs
index 8ce52ae58..af4aa44bb 100644
--- a/crypto/src/crypto/digests/RipeMD160Digest.cs
+++ b/crypto/src/crypto/digests/RipeMD160Digest.cs
@@ -1,5 +1,7 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Digests
 {
     /**
@@ -30,6 +32,13 @@ namespace Org.BouncyCastle.Crypto.Digests
         */
         public RipeMD160Digest(RipeMD160Digest t) : base(t)
         {
+			CopyIn(t);
+		}
+
+		private void CopyIn(RipeMD160Digest t)
+		{
+			base.CopyIn(t);
+
             H0 = t.H0;
             H1 = t.H1;
             H2 = t.H2;
@@ -418,6 +427,19 @@ namespace Org.BouncyCastle.Crypto.Digests
                 X[i] = 0;
             }
         }
+		
+		public override IMemoable Copy()
+		{
+			return new RipeMD160Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			RipeMD160Digest d = (RipeMD160Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 
 }
diff --git a/crypto/src/crypto/digests/RipeMD256Digest.cs b/crypto/src/crypto/digests/RipeMD256Digest.cs
index 950e94f80..306275767 100644
--- a/crypto/src/crypto/digests/RipeMD256Digest.cs
+++ b/crypto/src/crypto/digests/RipeMD256Digest.cs
@@ -1,5 +1,7 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Digests
 {
     /// <remarks>
@@ -37,6 +39,12 @@ namespace Org.BouncyCastle.Crypto.Digests
         /// </summary>
         public RipeMD256Digest(RipeMD256Digest t):base(t)
         {
+			CopyIn(t);
+		}
+
+		private void CopyIn(RipeMD256Digest t)
+		{
+			base.CopyIn(t);
 
             H0 = t.H0;
             H1 = t.H1;
@@ -405,5 +413,18 @@ namespace Org.BouncyCastle.Crypto.Digests
                 X[i] = 0;
             }
         }
+		
+		public override IMemoable Copy()
+		{
+			return new RipeMD256Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			RipeMD256Digest d = (RipeMD256Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 }
diff --git a/crypto/src/crypto/digests/RipeMD320Digest.cs b/crypto/src/crypto/digests/RipeMD320Digest.cs
index 25c74baef..767d74dba 100644
--- a/crypto/src/crypto/digests/RipeMD320Digest.cs
+++ b/crypto/src/crypto/digests/RipeMD320Digest.cs
@@ -1,5 +1,7 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Digests
 {
 	/// <remarks>
@@ -38,6 +40,12 @@ namespace Org.BouncyCastle.Crypto.Digests
         public RipeMD320Digest(RipeMD320Digest t)
 			: base(t)
         {
+			CopyIn(t);
+		}
+
+		private void CopyIn(RipeMD320Digest t)
+		{
+			base.CopyIn(t);
 
             H0 = t.H0;
             H1 = t.H1;
@@ -434,5 +442,18 @@ namespace Org.BouncyCastle.Crypto.Digests
                 X[i] = 0;
             }
         }
+		
+		public override IMemoable Copy()
+		{
+			return new RipeMD320Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			RipeMD320Digest d = (RipeMD320Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 }
diff --git a/crypto/src/crypto/digests/SHA3Digest.cs b/crypto/src/crypto/digests/SHA3Digest.cs
index a115495f4..2c6837b3c 100644
--- a/crypto/src/crypto/digests/SHA3Digest.cs
+++ b/crypto/src/crypto/digests/SHA3Digest.cs
@@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests
     /// Following the naming conventions used in the C source code to enable easy review of the implementation.
     /// </remarks>
     public class Sha3Digest
-        : IDigest
+        : IDigest, IMemoable
     {
         private static readonly ulong[] KeccakRoundConstants = KeccakInitializeRoundConstants();
 
@@ -103,6 +103,11 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         public Sha3Digest(Sha3Digest source)
         {
+			CopyIn(source);
+		}
+
+		private void CopyIn(Sha3Digest source)
+		{
             Array.Copy(source.state, 0, this.state, 0, source.state.Length);
             Array.Copy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.Length);
             this.rate = source.rate;
@@ -537,5 +542,19 @@ namespace Org.BouncyCastle.Crypto.Digests
         {
             Array.Copy(byteState, 0, data, 0, laneCount * 8);
         }
+
+		public IMemoable Copy()
+		{
+			return new Sha3Digest(this);
+		}
+
+		public void Reset(IMemoable other)
+		{
+			Sha3Digest d = (Sha3Digest)other;
+
+			CopyIn(d);
+		}
+
+
     }
 }
diff --git a/crypto/src/crypto/digests/Sha1Digest.cs b/crypto/src/crypto/digests/Sha1Digest.cs
index e72b84f94..60ec651d5 100644
--- a/crypto/src/crypto/digests/Sha1Digest.cs
+++ b/crypto/src/crypto/digests/Sha1Digest.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -33,6 +34,13 @@ namespace Org.BouncyCastle.Crypto.Digests
         public Sha1Digest(Sha1Digest t)
             : base(t)
         {
+			CopyIn(t);
+		}
+
+		private void CopyIn(Sha1Digest t)
+		{
+			base.CopyIn(t);
+
             H1 = t.H1;
             H2 = t.H2;
             H3 = t.H3;
@@ -259,5 +267,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             xOff = 0;
             Array.Clear(X, 0, 16);
         }
+		
+		public override IMemoable Copy()
+		{
+			return new Sha1Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			Sha1Digest d = (Sha1Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 }
diff --git a/crypto/src/crypto/digests/Sha224Digest.cs b/crypto/src/crypto/digests/Sha224Digest.cs
index 66ecd4ecd..b4e853745 100644
--- a/crypto/src/crypto/digests/Sha224Digest.cs
+++ b/crypto/src/crypto/digests/Sha224Digest.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -41,6 +42,13 @@ namespace Org.BouncyCastle.Crypto.Digests
 			 Sha224Digest t)
 			 : base(t)
         {
+			CopyIn(t);
+		}
+
+		private void CopyIn(Sha224Digest t)
+		{
+			base.CopyIn(t);
+
             H1 = t.H1;
             H2 = t.H2;
             H3 = t.H3;
@@ -264,5 +272,18 @@ namespace Org.BouncyCastle.Crypto.Digests
 			0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
             0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
         };
+		
+		public override IMemoable Copy()
+		{
+			return new Sha224Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			Sha224Digest d = (Sha224Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 }
diff --git a/crypto/src/crypto/digests/Sha256Digest.cs b/crypto/src/crypto/digests/Sha256Digest.cs
index 1c00ab71f..98e10a34d 100644
--- a/crypto/src/crypto/digests/Sha256Digest.cs
+++ b/crypto/src/crypto/digests/Sha256Digest.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -36,6 +37,13 @@ namespace Org.BouncyCastle.Crypto.Digests
         */
         public Sha256Digest(Sha256Digest t) : base(t)
         {
+			CopyIn(t);
+		}
+
+		private void CopyIn(Sha256Digest t)
+		{
+			base.CopyIn(t);
+
             H1 = t.H1;
             H2 = t.H2;
             H3 = t.H3;
@@ -305,5 +313,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
             0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
         };
+		
+		public override IMemoable Copy()
+		{
+			return new Sha256Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			Sha256Digest d = (Sha256Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 }
diff --git a/crypto/src/crypto/digests/Sha384Digest.cs b/crypto/src/crypto/digests/Sha384Digest.cs
index f1372d0a9..e6c9a9aa9 100644
--- a/crypto/src/crypto/digests/Sha384Digest.cs
+++ b/crypto/src/crypto/digests/Sha384Digest.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -83,5 +84,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             H7 = 0xdb0c2e0d64f98fa7;
             H8 = 0x47b5481dbefa4fa4;
         }
+		
+		public override IMemoable Copy()
+		{
+			return new Sha384Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			Sha384Digest d = (Sha384Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 }
diff --git a/crypto/src/crypto/digests/Sha512Digest.cs b/crypto/src/crypto/digests/Sha512Digest.cs
index b38ff8ee7..2a0964fd3 100644
--- a/crypto/src/crypto/digests/Sha512Digest.cs
+++ b/crypto/src/crypto/digests/Sha512Digest.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -86,5 +87,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             H7 = 0x1f83d9abfb41bd6b;
             H8 = 0x5be0cd19137e2179;
         }
+		
+		public override IMemoable Copy()
+		{
+			return new Sha512Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			Sha512Digest d = (Sha512Digest)other;
+
+			CopyIn(d);
+		}
+
     }
 }
diff --git a/crypto/src/crypto/digests/Sha512tDigest.cs b/crypto/src/crypto/digests/Sha512tDigest.cs
index 7580d6267..2caefa763 100644
--- a/crypto/src/crypto/digests/Sha512tDigest.cs
+++ b/crypto/src/crypto/digests/Sha512tDigest.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -44,14 +45,7 @@ namespace Org.BouncyCastle.Crypto.Digests
         {
             this.digestLength = t.digestLength;
 
-            this.H1t = t.H1t;
-            this.H2t = t.H2t;
-            this.H3t = t.H3t;
-            this.H4t = t.H4t;
-            this.H5t = t.H5t;
-            this.H6t = t.H6t;
-            this.H7t = t.H7t;
-            this.H8t = t.H8t;
+			Reset(t);
         }
 
         public override string AlgorithmName
@@ -175,5 +169,32 @@ namespace Org.BouncyCastle.Crypto.Digests
                 bs[off + num] = (byte)(n >> shift);
             }
         }
-    }
+
+		public override IMemoable Copy()
+		{
+			return new Sha512tDigest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			Sha512tDigest t = (Sha512tDigest)other;
+
+			if (this.digestLength != t.digestLength)
+			{
+				throw new MemoableResetException("digestLength inappropriate in other");
+			}
+
+			base.CopyIn(t);
+
+			this.H1t = t.H1t;
+			this.H2t = t.H2t;
+			this.H3t = t.H3t;
+			this.H4t = t.H4t;
+			this.H5t = t.H5t;
+			this.H6t = t.H6t;
+			this.H7t = t.H7t;
+			this.H8t = t.H8t;
+		}
+
+	}
 }
diff --git a/crypto/src/crypto/digests/SkeinDigest.cs b/crypto/src/crypto/digests/SkeinDigest.cs
new file mode 100644
index 000000000..f826ce503
--- /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..49ec51304
--- /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 ((BlockSize != s.BlockSize) || (outputSizeBytes != s.outputSizeBytes))
+			{
+				throw new MemoableResetException("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/digests/Sm3Digest.cs b/crypto/src/crypto/digests/Sm3Digest.cs
new file mode 100644
index 000000000..7a9e6db36
--- /dev/null
+++ b/crypto/src/crypto/digests/Sm3Digest.cs
@@ -0,0 +1,328 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+
+	/// <summary>
+	/// Implementation of Chinese SM3 digest as described at
+	/// http://tools.ietf.org/html/draft-shen-sm3-hash-00
+	/// and at .... ( Chinese PDF )
+	/// </summary>
+	/// <remarks>
+	/// The specification says "process a bit stream",
+	/// but this is written to process bytes in blocks of 4,
+	/// meaning this will process 32-bit word groups.
+	/// But so do also most other digest specifications,
+	/// including the SHA-256 which was a origin for
+	/// this specification.
+	/// </remarks>
+	public class Sm3Digest
+		: GeneralDigest
+	{
+		private const int DIGEST_LENGTH = 32;   // bytes
+		private const int BLOCK_SIZE = 64 / 4; // of 32 bit ints (16 ints)
+
+		private uint[] V = new uint[DIGEST_LENGTH / 4]; // in 32 bit ints (8 ints)
+		private uint[] inwords = new uint[BLOCK_SIZE];
+		private int xOff;
+
+		// Work-bufs used within processBlock()
+		private uint[] W = new uint[68];
+		private uint[] W1 = new uint[64];
+
+		// Round constant T for processBlock() which is 32 bit integer rolled left up to (63 MOD 32) bit positions.
+		private static readonly uint[] T = new uint[64];
+
+		static Sm3Digest()
+		{
+			for (int i = 0; i < 16; ++i)
+			{
+				uint t = 0x79CC4519;
+				T[i] = (t << i) | (t >> (32 - i));
+			}
+			for (int i = 16; i < 64; ++i)
+			{
+				int n = i % 32;
+				uint t = 0x7A879D8A;
+				T[i] = (t << n) | (t >> (32 - n));
+			}
+		}
+
+
+		/// <summary>
+		/// Standard constructor
+		/// </summary>
+		public Sm3Digest()
+		{
+			Reset();
+		}
+
+		/// <summary>
+		/// Copy constructor.  This will copy the state of the provided
+		/// message digest.
+		/// </summary>
+		public Sm3Digest(Sm3Digest t)
+			: base(t)
+		{
+			CopyIn(t);
+		}
+
+		private void CopyIn(Sm3Digest t)
+		{
+			Array.Copy(t.V, 0, this.V, 0, this.V.Length);
+			Array.Copy(t.inwords, 0, this.inwords, 0, this.inwords.Length);
+			xOff = t.xOff;
+		}
+
+		public override string AlgorithmName
+		{
+			get { return "SM3"; }
+		}
+
+		public override int GetDigestSize()
+		{
+			return DIGEST_LENGTH;
+		}
+
+		public override IMemoable Copy()
+		{
+			return new Sm3Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			Sm3Digest d = (Sm3Digest)other;
+
+			base.CopyIn(d);
+			CopyIn(d);
+		}
+
+		/// <summary>
+		/// reset the chaining variables
+		/// </summary>
+		public override void Reset()
+		{
+			base.Reset();
+
+			this.V[0] = 0x7380166F;
+			this.V[1] = 0x4914B2B9;
+			this.V[2] = 0x172442D7;
+			this.V[3] = 0xDA8A0600;
+			this.V[4] = 0xA96F30BC;
+			this.V[5] = 0x163138AA;
+			this.V[6] = 0xE38DEE4D;
+			this.V[7] = 0xB0FB0E4E;
+
+			this.xOff = 0;
+		}
+
+
+		public override int DoFinal(byte[] output, int outOff)
+		{
+			Finish();
+
+			Pack.UInt32_To_BE(this.V[0], output, outOff + 0);
+			Pack.UInt32_To_BE(this.V[1], output, outOff + 4);
+			Pack.UInt32_To_BE(this.V[2], output, outOff + 8);
+			Pack.UInt32_To_BE(this.V[3], output, outOff + 12);
+			Pack.UInt32_To_BE(this.V[4], output, outOff + 16);
+			Pack.UInt32_To_BE(this.V[5], output, outOff + 20);
+			Pack.UInt32_To_BE(this.V[6], output, outOff + 24);
+			Pack.UInt32_To_BE(this.V[7], output, outOff + 28);
+
+			Reset();
+
+			return DIGEST_LENGTH;
+		}
+
+
+		internal override void ProcessWord(byte[] input,
+		                                   int inOff)
+		{
+			uint n = Pack.BE_To_UInt32(input, inOff);
+			this.inwords[this.xOff] = n;
+			++this.xOff;
+
+			if (this.xOff >= 16)
+			{
+				ProcessBlock();
+			}
+		}
+
+		internal override void ProcessLength(long bitLength)
+		{
+			if (this.xOff > (BLOCK_SIZE - 2))
+			{
+				// xOff == 15  --> can't fit the 64 bit length field at tail..
+				this.inwords[this.xOff] = 0; // fill with zero
+				++this.xOff;
+
+				ProcessBlock();
+			}
+			// Fill with zero words, until reach 2nd to last slot
+			while (this.xOff < (BLOCK_SIZE - 2))
+			{
+				this.inwords[this.xOff] = 0;
+				++this.xOff;
+			}
+
+			// Store input data length in BITS
+			this.inwords[this.xOff++] = (uint)(bitLength >> 32);
+			this.inwords[this.xOff++] = (uint)(bitLength);
+		}
+
+		/*
+
+	3.4.2.  Constants
+
+
+	   Tj = 79cc4519        when 0  < = j < = 15
+	   Tj = 7a879d8a        when 16 < = j < = 63
+
+	3.4.3.  Boolean function
+
+
+	   FFj(X;Y;Z) = X XOR Y XOR Z                       when 0  < = j < = 15
+	              = (X AND Y) OR (X AND Z) OR (Y AND Z) when 16 < = j < = 63
+
+	   GGj(X;Y;Z) = X XOR Y XOR Z                       when 0  < = j < = 15
+	              = (X AND Y) OR (NOT X AND Z)          when 16 < = j < = 63
+
+	   The X, Y, Z in the fomular are words!GBP
+
+	3.4.4.  Permutation function
+
+
+	   P0(X) = X XOR (X <<<  9) XOR (X <<< 17)   ## ROLL, not SHIFT
+	   P1(X) = X XOR (X <<< 15) XOR (X <<< 23)   ## ROLL, not SHIFT
+
+	   The X in the fomular are a word.
+
+	----------
+
+	Each ROLL converted to Java expression:
+
+	ROLL 9  :  ((x <<  9) | (x >> (32-9))))
+	ROLL 17 :  ((x << 17) | (x >> (32-17)))
+	ROLL 15 :  ((x << 15) | (x >> (32-15)))
+	ROLL 23 :  ((x << 23) | (x >> (32-23)))
+
+	 */
+
+		private uint P0(uint x)
+		{
+			uint r9 = ((x << 9) | (x >> (32 - 9)));
+			uint r17 = ((x << 17) | (x >> (32 - 17)));
+			return (x ^ r9 ^ r17);
+		}
+
+		private uint P1(uint x)
+		{
+			uint r15 = ((x << 15) | (x >> (32 - 15)));
+			uint r23 = ((x << 23) | (x >> (32 - 23)));
+			return (x ^ r15 ^ r23);
+		}
+
+		private uint FF0(uint x, uint y, uint z)
+		{
+			return (x ^ y ^ z);
+		}
+
+		private uint FF1(uint x, uint y, uint z)
+		{
+			return ((x & y) | (x & z) | (y & z));
+		}
+
+		private uint GG0(uint x, uint y, uint z)
+		{
+			return (x ^ y ^ z);
+		}
+
+		private uint GG1(uint x, uint y, uint z)
+		{
+			return ((x & y) | ((~x) & z));
+		}
+
+
+		internal override void ProcessBlock()
+		{
+			for (int j = 0; j < 16; ++j)
+			{
+				this.W[j] = this.inwords[j];
+			}
+			for (int j = 16; j < 68; ++j)
+			{
+				uint wj3 = this.W[j - 3];
+				uint r15 = ((wj3 << 15) | (wj3 >> (32 - 15)));
+				uint wj13 = this.W[j - 13];
+				uint r7 = ((wj13 << 7) | (wj13 >> (32 - 7)));
+				this.W[j] = P1(this.W[j - 16] ^ this.W[j - 9] ^ r15) ^ r7 ^ this.W[j - 6];
+			}
+			for (int j = 0; j < 64; ++j)
+			{
+				this.W1[j] = this.W[j] ^ this.W[j + 4];
+			}
+
+			uint A = this.V[0];
+			uint B = this.V[1];
+			uint C = this.V[2];
+			uint D = this.V[3];
+			uint E = this.V[4];
+			uint F = this.V[5];
+			uint G = this.V[6];
+			uint H = this.V[7];
+
+
+			for (int j = 0; j < 16; ++j)
+			{
+				uint a12 = ((A << 12) | (A >> (32 - 12)));
+				uint s1_ = a12 + E + T[j];
+				uint SS1 = ((s1_ << 7) | (s1_ >> (32 - 7)));
+				uint SS2 = SS1 ^ a12;
+				uint TT1 = FF0(A, B, C) + D + SS2 + this.W1[j];
+				uint TT2 = GG0(E, F, G) + H + SS1 + this.W[j];
+				D = C;
+				C = ((B << 9) | (B >> (32 - 9)));
+				B = A;
+				A = TT1;
+				H = G;
+				G = ((F << 19) | (F >> (32 - 19)));
+				F = E;
+				E = P0(TT2);
+			}
+
+			// Different FF,GG functions on rounds 16..63
+			for (int j = 16; j < 64; ++j)
+			{
+				uint a12 = ((A << 12) | (A >> (32 - 12)));
+				uint s1_ = a12 + E + T[j];
+				uint SS1 = ((s1_ << 7) | (s1_ >> (32 - 7)));
+				uint SS2 = SS1 ^ a12;
+				uint TT1 = FF1(A, B, C) + D + SS2 + this.W1[j];
+				uint TT2 = GG1(E, F, G) + H + SS1 + this.W[j];
+				D = C;
+				C = ((B << 9) | (B >> (32 - 9)));
+				B = A;
+				A = TT1;
+				H = G;
+				G = ((F << 19) | (F >> (32 - 19)));
+				F = E;
+				E = P0(TT2);
+			}
+
+			this.V[0] ^= A;
+			this.V[1] ^= B;
+			this.V[2] ^= C;
+			this.V[3] ^= D;
+			this.V[4] ^= E;
+			this.V[5] ^= F;
+			this.V[6] ^= G;
+			this.V[7] ^= H;
+
+			this.xOff = 0;
+		}
+	}
+}
\ No newline at end of file
diff --git a/crypto/src/crypto/digests/TigerDigest.cs b/crypto/src/crypto/digests/TigerDigest.cs
index b8c9a7664..059232de0 100644
--- a/crypto/src/crypto/digests/TigerDigest.cs
+++ b/crypto/src/crypto/digests/TigerDigest.cs
@@ -1,5 +1,7 @@
 using System;
+
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -9,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests
     *  http://www.cs.technion.ac.il/~biham/Reports/Tiger</a>
     */
     public class TigerDigest
-		: IDigest
+		: IDigest, IMemoable
     {
         private const int MyByteLength = 64;
 
@@ -571,17 +573,7 @@ namespace Org.BouncyCastle.Crypto.Digests
         */
         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;
+			Reset(t);
         }
 
 		public string AlgorithmName
@@ -864,5 +856,28 @@ namespace Org.BouncyCastle.Crypto.Digests
 
             byteCount = 0;
         }
-    }
+
+		public IMemoable Copy()
+		{
+			return new TigerDigest(this);
+		}
+
+		public void Reset(IMemoable other)
+		{
+			TigerDigest t = (TigerDigest)other;
+
+			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;
+		}    
+
+	}
 }
diff --git a/crypto/src/crypto/digests/WhirlpoolDigest.cs b/crypto/src/crypto/digests/WhirlpoolDigest.cs
index df83f4508..55b71205e 100644
--- a/crypto/src/crypto/digests/WhirlpoolDigest.cs
+++ b/crypto/src/crypto/digests/WhirlpoolDigest.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -9,7 +10,8 @@ namespace Org.BouncyCastle.Crypto.Digests
 	* and Rijmen.
 	*
 	*/
-	public sealed class WhirlpoolDigest : IDigest
+	public sealed class WhirlpoolDigest
+		: IDigest, IMemoable
 	{
 		private const int BYTE_LENGTH = 64;
 
@@ -149,19 +151,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 			*/
 		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);
+			Reset(originalDigest);
 		}
 
 		public string AlgorithmName
@@ -393,5 +383,31 @@ namespace Org.BouncyCastle.Crypto.Digests
 		{
 			return BYTE_LENGTH;
 		}
+
+		public IMemoable Copy()
+		{
+			return new WhirlpoolDigest(this);
+		}
+
+		public void Reset(IMemoable other)
+		{
+			WhirlpoolDigest originalDigest = (WhirlpoolDigest)other;
+
+			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);
+		}
+
+
 	}
 }
diff --git a/crypto/src/crypto/engines/ThreefishEngine.cs b/crypto/src/crypto/engines/ThreefishEngine.cs
new file mode 100644
index 000000000..3d4ee8835
--- /dev/null
+++ b/crypto/src/crypto/engines/ThreefishEngine.cs
@@ -0,0 +1,1491 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+
+	/// <summary>
+	/// Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block
+	/// sizes.
+	/// </summary>
+	/// <remarks>
+	/// This is the 1.3 version of Threefish defined in the Skein hash function submission to the NIST
+	/// SHA-3 competition in October 2010.
+	/// <p/>
+	/// Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+	/// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+	/// <p/>
+	/// This implementation inlines all round functions, unrolls 8 rounds, and uses 1.2k of static tables
+	/// to speed up key schedule injection. <br>
+	/// 2 x block size state is retained by each cipher instance.
+	/// </remarks>
+	public class ThreefishEngine
+		: IBlockCipher
+	{
+		/// <summary>
+		/// 256 bit block size - Threefish-256
+		/// </summary>
+		public const int BLOCKSIZE_256 = 256;
+		/// <summary>
+		/// 512 bit block size - Threefish-512
+		/// </summary>
+		public const int BLOCKSIZE_512 = 512;
+		/// <summary>
+		/// 1024 bit block size - Threefish-1024
+		/// </summary>
+		public const int BLOCKSIZE_1024 = 1024;
+
+		/**
+	     * Size of the tweak in bytes (always 128 bit/16 bytes)
+	     */
+		private const int TWEAK_SIZE_BYTES = 16;
+		private const int TWEAK_SIZE_WORDS = TWEAK_SIZE_BYTES / 8;
+
+		/**
+	     * Rounds in Threefish-256
+	     */
+		private const int ROUNDS_256 = 72;
+		/**
+	     * Rounds in Threefish-512
+	     */
+		private const int ROUNDS_512 = 72;
+		/**
+	     * Rounds in Threefish-1024
+	     */
+		private const int ROUNDS_1024 = 80;
+
+		/**
+	     * Max rounds of any of the variants
+	     */
+		private const int MAX_ROUNDS = ROUNDS_1024;
+
+		/**
+	     * Key schedule parity constant
+	     */
+		private const ulong C_240 = 0x1BD11BDAA9FC1A22L;
+
+		/* Pre-calculated modulo arithmetic tables for key schedule lookups */
+		private static readonly int[] MOD9 = new int[MAX_ROUNDS];
+		private static readonly int[] MOD17 = new int[MOD9.Length];
+		private static readonly int[] MOD5 = new int[MOD9.Length];
+		private static readonly int[] MOD3 = new int[MOD9.Length];
+
+		static ThreefishEngine()
+		{
+			for (int i = 0; i < MOD9.Length; i++)
+			{
+				MOD17[i] = i % 17;
+				MOD9[i] = i % 9;
+				MOD5[i] = i % 5;
+				MOD3[i] = i % 3;
+			}
+		}
+
+		/**
+	     * Block size in bytes
+	     */
+		private readonly int blocksizeBytes;
+
+		/**
+	     * Block size in 64 bit words
+	     */
+		private readonly int blocksizeWords;
+
+		/**
+	     * Buffer for byte oriented processBytes to call internal word API
+	     */
+		private readonly ulong[] currentBlock;
+
+		/**
+	     * Tweak bytes (2 byte t1,t2, calculated t3 and repeat of t1,t2 for modulo free lookup
+	     */
+		private readonly ulong[] t = new ulong[5];
+
+		/**
+	     * Key schedule words
+	     */
+		private readonly ulong[] kw;
+
+		/**
+	     * The internal cipher implementation (varies by blocksize)
+	     */
+		private readonly ThreefishCipher cipher;
+
+		private bool forEncryption;
+
+		/// <summary>
+		/// Constructs a new Threefish cipher, with a specified block size.
+		/// </summary>
+		/// <param name="blocksizeBits">the block size in bits, one of <see cref="BLOCKSIZE_256"/>, <see cref="BLOCKSIZE_512"/>,
+		///                      <see cref="BLOCKSIZE_1024"/> .</param>
+		public ThreefishEngine(int blocksizeBits)
+		{
+			this.blocksizeBytes = (blocksizeBits / 8);
+			this.blocksizeWords = (this.blocksizeBytes / 8);
+			this.currentBlock = new ulong[blocksizeWords];
+
+			/*
+	         * Provide room for original key words, extended key word and repeat of key words for modulo
+	         * free lookup of key schedule words.
+	         */
+			this.kw = new ulong[2 * blocksizeWords + 1];
+
+			switch (blocksizeBits)
+			{
+				case BLOCKSIZE_256:
+				cipher = new Threefish256Cipher(kw, t);
+				break;
+				case BLOCKSIZE_512:
+				cipher = new Threefish512Cipher(kw, t);
+				break;
+				case BLOCKSIZE_1024:
+				cipher = new Threefish1024Cipher(kw, t);
+				break;
+				default:
+				throw new ArgumentException(
+					"Invalid blocksize - Threefish is defined with block size of 256, 512, or 1024 bits");
+			}
+		}
+
+		/// <summary>
+		/// Initialise the engine.
+		/// </summary>
+		/// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
+		/// <param name="parameters">an instance of <see cref="TweakableBlockCipherParameters"/> or <see cref="KeyParameter"/> (to
+		///               use a 0 tweak)</param>
+		public void Init(bool forEncryption, ICipherParameters parameters)
+		{
+			byte[] keyBytes;
+			byte[] tweakBytes;
+
+			if (parameters is TweakableBlockCipherParameters)
+			{
+				TweakableBlockCipherParameters tParams = (TweakableBlockCipherParameters)parameters;
+				keyBytes = tParams.Key.GetKey();
+				tweakBytes = tParams.Tweak;
+			}
+			else if (parameters is KeyParameter)
+			{
+				keyBytes = ((KeyParameter)parameters).GetKey();
+				tweakBytes = null;
+			}
+			else
+			{
+				throw new ArgumentException("Invalid parameter passed to Threefish init - "
+				                            + parameters.GetType().Name);
+			}
+
+			ulong[] keyWords = null;
+			ulong[] tweakWords = null;
+
+			if (keyBytes != null)
+			{
+				if (keyBytes.Length != this.blocksizeBytes)
+				{
+					throw new ArgumentException("Threefish key must be same size as block (" + blocksizeBytes
+					                            + " bytes)");
+				}
+				keyWords = new ulong[blocksizeWords];
+				for (int i = 0; i < keyWords.Length; i++)
+				{
+					keyWords[i] = BytesToWord(keyBytes, i * 8);
+				}
+			}
+			if (tweakBytes != null)
+			{
+				if (tweakBytes.Length != TWEAK_SIZE_BYTES)
+				{
+					throw new ArgumentException("Threefish tweak must be " + TWEAK_SIZE_BYTES + " bytes");
+				}
+				tweakWords = new ulong[]{BytesToWord(tweakBytes, 0), BytesToWord(tweakBytes, 8)};
+			}
+			Init(forEncryption, keyWords, tweakWords);
+		}
+
+		/// <summary>
+		/// Initialise the engine, specifying the key and tweak directly.
+		/// </summary>
+		/// <param name="forEncryption">the cipher mode.</param>
+		/// <param name="key">the words of the key, or <code>null</code> to use the current key.</param>
+		/// <param name="tweak">the 2 word (128 bit) tweak, or <code>null</code> to use the current tweak.</param>
+		internal void Init(bool forEncryption, ulong[] key, ulong[] tweak)
+		{
+			this.forEncryption = forEncryption;
+			if (key != null)
+			{
+				SetKey(key);
+			}
+			if (tweak != null)
+			{
+				SetTweak(tweak);
+			}
+		}
+
+		private void SetKey(ulong[] key)
+		{
+			if (key.Length != this.blocksizeWords)
+			{
+				throw new ArgumentException("Threefish key must be same size as block (" + blocksizeWords
+				                            + " words)");
+			}
+
+			/*
+	         * Full subkey schedule is deferred to execution to avoid per cipher overhead (10k for 512,
+	         * 20k for 1024).
+	         * 
+	         * Key and tweak word sequences are repeated, and static MOD17/MOD9/MOD5/MOD3 calculations
+	         * used, to avoid expensive mod computations during cipher operation.
+	         */
+
+			ulong knw = C_240;
+			for (int i = 0; i < blocksizeWords; i++)
+			{
+				kw[i] = key[i];
+				knw = knw ^ kw[i];
+			}
+			kw[blocksizeWords] = knw;
+			Array.Copy(kw, 0, kw, blocksizeWords + 1, blocksizeWords);
+		}
+
+		private void SetTweak(ulong[] tweak)
+		{
+			if (tweak.Length != TWEAK_SIZE_WORDS)
+			{
+				throw new ArgumentException("Tweak must be " + TWEAK_SIZE_WORDS + " words.");
+			}
+
+			/*
+	         * Tweak schedule partially repeated to avoid mod computations during cipher operation
+	         */
+			t[0] = tweak[0];
+			t[1] = tweak[1];
+			t[2] = t[0] ^ t[1];
+			t[3] = t[0];
+			t[4] = t[1];
+		}
+
+		public string AlgorithmName
+		{
+			get { return "Threefish-" + (blocksizeBytes * 8); }
+		}
+
+		public bool IsPartialBlockOkay
+		{
+			get { return false; }
+		}
+
+		public int GetBlockSize()
+		{
+			return blocksizeBytes;
+		}
+
+		public void Reset()
+		{
+		}
+
+		public int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
+		{
+			if ((outOff + blocksizeBytes) > outBytes.Length)
+			{
+				throw new DataLengthException("Output buffer too short");
+			}
+
+			if ((inOff + blocksizeBytes) > inBytes.Length)
+			{
+				throw new DataLengthException("Input buffer too short");
+			}
+
+			for (int i = 0; i < blocksizeBytes; i += 8)
+			{
+				currentBlock[i >> 3] = BytesToWord(inBytes, inOff + i);
+			}
+			ProcessBlock(this.currentBlock, this.currentBlock);
+			for (int i = 0; i < blocksizeBytes; i += 8)
+			{
+				WordToBytes(this.currentBlock[i >> 3], outBytes, outOff + i);
+			}
+
+			return blocksizeBytes;
+		}
+
+		/// <summary>
+		/// Process a block of data represented as 64 bit words.
+		/// </summary>
+		/// <returns>the number of 8 byte words processed (which will be the same as the block size).</returns>
+		/// <param name="inWords">a block sized buffer of words to process.</param>
+		/// <param name="outWords">a block sized buffer of words to receive the output of the operation.</param>
+		/// <exception cref="DataLengthException">if either the input or output is not block sized</exception>
+		/// <exception cref="InvalidOperationException">if this engine is not initialised</exception>
+		internal int ProcessBlock(ulong[] inWords, ulong[] outWords)
+		{
+			if (kw[blocksizeWords] == 0)
+			{
+				throw new InvalidOperationException("Threefish engine not initialised");
+			}
+
+			if (inWords.Length != blocksizeWords)
+			{
+				throw new DataLengthException("Input buffer too short");
+			}
+			if (outWords.Length != blocksizeWords)
+			{
+				throw new DataLengthException("Output buffer too short");
+			}
+
+			if (forEncryption)
+			{
+				cipher.EncryptBlock(inWords, outWords);
+			}
+			else
+			{
+				cipher.DecryptBlock(inWords, outWords);
+			}
+
+			return blocksizeWords;
+		}
+
+		/// <summary>
+		/// Read a single 64 bit word from input in LSB first order.
+		/// </summary>
+		internal static ulong BytesToWord(byte[] bytes, int off)
+		{
+			if ((off + 8) > bytes.Length)
+			{
+				// Help the JIT avoid index checks
+				throw new ArgumentException();
+			}
+
+			ulong word = 0;
+			int index = off;
+
+			word = (bytes[index++] & 0xffUL);
+			word |= (bytes[index++] & 0xffUL) << 8;
+			word |= (bytes[index++] & 0xffUL) << 16;
+			word |= (bytes[index++] & 0xffUL) << 24;
+			word |= (bytes[index++] & 0xffUL) << 32;
+			word |= (bytes[index++] & 0xffUL) << 40;
+			word |= (bytes[index++] & 0xffUL) << 48;
+			word |= (bytes[index++] & 0xffUL) << 56;
+
+			return word;
+		}
+
+		/// <summary>
+		/// Write a 64 bit word to output in LSB first order.
+		/// </summary>
+		internal static void WordToBytes(ulong word, byte[] bytes, int off)
+		{
+			if ((off + 8) > bytes.Length)
+			{
+				// Help the JIT avoid index checks
+				throw new ArgumentException();
+			}
+			int index = off;
+
+			bytes[index++] = (byte)word;
+			bytes[index++] = (byte)(word >> 8);
+			bytes[index++] = (byte)(word >> 16);
+			bytes[index++] = (byte)(word >> 24);
+			bytes[index++] = (byte)(word >> 32);
+			bytes[index++] = (byte)(word >> 40);
+			bytes[index++] = (byte)(word >> 48);
+			bytes[index++] = (byte)(word >> 56);
+		}
+
+		/**
+	     * Rotate left + xor part of the mix operation.
+	     */
+		private static ulong RotlXor(ulong x, int n, ulong xor)
+		{
+			return ((x << n) | (x >> (64 - n))) ^ xor;
+		}
+
+		/**
+	     * Rotate xor + rotate right part of the unmix operation.
+	     */
+		private static ulong XorRotr(ulong x, int n, ulong xor)
+		{
+			ulong xored = x ^ xor;
+			return (xored >> n) | (xored << (64 - n));
+		}
+
+		private abstract class ThreefishCipher
+		{
+			/**
+	         * The extended + repeated tweak words
+	         */
+			protected readonly ulong[] t;
+			/**
+	         * The extended + repeated key words
+	         */
+			protected readonly ulong[] kw;
+
+			protected ThreefishCipher(ulong[] kw, ulong[] t)
+			{
+				this.kw = kw;
+				this.t = t;
+			}
+
+			internal abstract void EncryptBlock(ulong[] block, ulong[] outWords);
+
+			internal abstract void DecryptBlock(ulong[] block, ulong[] outWords);
+
+		}
+
+		private sealed class Threefish256Cipher
+			: ThreefishCipher
+		{
+			/**
+	         * Mix rotation constants defined in Skein 1.3 specification
+	         */
+			private const int ROTATION_0_0 = 14, ROTATION_0_1 = 16;
+			private const int ROTATION_1_0 = 52, ROTATION_1_1 = 57;
+			private const int ROTATION_2_0 = 23, ROTATION_2_1 = 40;
+			private const int ROTATION_3_0 = 5, ROTATION_3_1 = 37;
+
+			private const int ROTATION_4_0 = 25, ROTATION_4_1 = 33;
+			private const int ROTATION_5_0 = 46, ROTATION_5_1 = 12;
+			private const int ROTATION_6_0 = 58, ROTATION_6_1 = 22;
+			private const int ROTATION_7_0 = 32, ROTATION_7_1 = 32;
+
+			public Threefish256Cipher(ulong[] kw, ulong[] t)
+				: base(kw, t)
+			{
+			}
+
+			internal override void EncryptBlock(ulong[] block, ulong[] outWords)
+			{
+				ulong[] kw = this.kw;
+				ulong[] t = this.t;
+				int[] mod5 = MOD5;
+				int[] mod3 = MOD3;
+
+				/* Help the JIT avoid index bounds checks */
+				if (kw.Length != 9)
+				{
+					throw new ArgumentException();
+				}
+				if (t.Length != 5)
+				{
+					throw new ArgumentException();
+				}
+
+				/*
+	             * Read 4 words of plaintext data, not using arrays for cipher state
+	             */
+				ulong b0 = block[0];
+				ulong b1 = block[1];
+				ulong b2 = block[2];
+				ulong b3 = block[3];
+
+				/*
+	             * First subkey injection.
+	             */
+				b0 += kw[0];
+				b1 += kw[1] + t[0];
+				b2 += kw[2] + t[1];
+				b3 += kw[3];
+
+				/*
+	             * Rounds loop, unrolled to 8 rounds per iteration.
+	             * 
+	             * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+	             * inlining of the permutations, which cycle every of 2 rounds (avoiding array
+	             * index/lookup).
+	             * 
+	             * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+	             * inlining constant rotation values (avoiding array index/lookup).
+	             */
+
+				for (int d = 1; d < (ROUNDS_256 / 4); d += 2)
+				{
+					int dm5 = mod5[d];
+					int dm3 = mod3[d];
+
+					/*
+	                 * 4 rounds of mix and permute.
+	                 * 
+	                 * Permute schedule has a 2 round cycle, so permutes are inlined in the mix
+	                 * operations in each 4 round block.
+	                 */
+					b1 = RotlXor(b1, ROTATION_0_0, b0 += b1);
+					b3 = RotlXor(b3, ROTATION_0_1, b2 += b3);
+
+					b3 = RotlXor(b3, ROTATION_1_0, b0 += b3);
+					b1 = RotlXor(b1, ROTATION_1_1, b2 += b1);
+
+					b1 = RotlXor(b1, ROTATION_2_0, b0 += b1);
+					b3 = RotlXor(b3, ROTATION_2_1, b2 += b3);
+
+					b3 = RotlXor(b3, ROTATION_3_0, b0 += b3);
+					b1 = RotlXor(b1, ROTATION_3_1, b2 += b1);
+
+					/*
+	                 * Subkey injection for first 4 rounds.
+	                 */
+					b0 += kw[dm5];
+					b1 += kw[dm5 + 1] + t[dm3];
+					b2 += kw[dm5 + 2] + t[dm3 + 1];
+					b3 += kw[dm5 + 3] + (uint)d;
+
+					/*
+	                 * 4 more rounds of mix/permute
+	                 */
+					b1 = RotlXor(b1, ROTATION_4_0, b0 += b1);
+					b3 = RotlXor(b3, ROTATION_4_1, b2 += b3);
+
+					b3 = RotlXor(b3, ROTATION_5_0, b0 += b3);
+					b1 = RotlXor(b1, ROTATION_5_1, b2 += b1);
+
+					b1 = RotlXor(b1, ROTATION_6_0, b0 += b1);
+					b3 = RotlXor(b3, ROTATION_6_1, b2 += b3);
+
+					b3 = RotlXor(b3, ROTATION_7_0, b0 += b3);
+					b1 = RotlXor(b1, ROTATION_7_1, b2 += b1);
+
+					/*
+	                 * Subkey injection for next 4 rounds.
+	                 */
+					b0 += kw[dm5 + 1];
+					b1 += kw[dm5 + 2] + t[dm3 + 1];
+					b2 += kw[dm5 + 3] + t[dm3 + 2];
+					b3 += kw[dm5 + 4] + (uint)d + 1;
+				}
+
+				/*
+	             * Output cipher state.
+	             */
+				outWords[0] = b0;
+				outWords[1] = b1;
+				outWords[2] = b2;
+				outWords[3] = b3;
+			}
+
+			internal override void DecryptBlock(ulong[] block, ulong[] state)
+			{
+				ulong[] kw = this.kw;
+				ulong[] t = this.t;
+				int[] mod5 = MOD5;
+				int[] mod3 = MOD3;
+
+				/* Help the JIT avoid index bounds checks */
+				if (kw.Length != 9)
+				{
+					throw new ArgumentException();
+				}
+				if (t.Length != 5)
+				{
+					throw new ArgumentException();
+				}
+
+				ulong b0 = block[0];
+				ulong b1 = block[1];
+				ulong b2 = block[2];
+				ulong b3 = block[3];
+
+				for (int d = (ROUNDS_256 / 4) - 1; d >= 1; d -= 2)
+				{
+					int dm5 = mod5[d];
+					int dm3 = mod3[d];
+
+					/* Reverse key injection for second 4 rounds */
+					b0 -= kw[dm5 + 1];
+					b1 -= kw[dm5 + 2] + t[dm3 + 1];
+					b2 -= kw[dm5 + 3] + t[dm3 + 2];
+					b3 -= kw[dm5 + 4] + (uint)d + 1;
+
+					/* Reverse second 4 mix/permute rounds */
+
+					b3 = XorRotr(b3, ROTATION_7_0, b0);
+					b0 -= b3;
+					b1 = XorRotr(b1, ROTATION_7_1, b2);
+					b2 -= b1;
+
+					b1 = XorRotr(b1, ROTATION_6_0, b0);
+					b0 -= b1;
+					b3 = XorRotr(b3, ROTATION_6_1, b2);
+					b2 -= b3;
+
+					b3 = XorRotr(b3, ROTATION_5_0, b0);
+					b0 -= b3;
+					b1 = XorRotr(b1, ROTATION_5_1, b2);
+					b2 -= b1;
+
+					b1 = XorRotr(b1, ROTATION_4_0, b0);
+					b0 -= b1;
+					b3 = XorRotr(b3, ROTATION_4_1, b2);
+					b2 -= b3;
+
+					/* Reverse key injection for first 4 rounds */
+					b0 -= kw[dm5];
+					b1 -= kw[dm5 + 1] + t[dm3];
+					b2 -= kw[dm5 + 2] + t[dm3 + 1];
+					b3 -= kw[dm5 + 3] + (uint)d;
+
+					/* Reverse first 4 mix/permute rounds */
+					b3 = XorRotr(b3, ROTATION_3_0, b0);
+					b0 -= b3;
+					b1 = XorRotr(b1, ROTATION_3_1, b2);
+					b2 -= b1;
+
+					b1 = XorRotr(b1, ROTATION_2_0, b0);
+					b0 -= b1;
+					b3 = XorRotr(b3, ROTATION_2_1, b2);
+					b2 -= b3;
+
+					b3 = XorRotr(b3, ROTATION_1_0, b0);
+					b0 -= b3;
+					b1 = XorRotr(b1, ROTATION_1_1, b2);
+					b2 -= b1;
+
+					b1 = XorRotr(b1, ROTATION_0_0, b0);
+					b0 -= b1;
+					b3 = XorRotr(b3, ROTATION_0_1, b2);
+					b2 -= b3;
+				}
+
+				/*
+	             * First subkey uninjection.
+	             */
+				b0 -= kw[0];
+				b1 -= kw[1] + t[0];
+				b2 -= kw[2] + t[1];
+				b3 -= kw[3];
+
+				/*
+	             * Output cipher state.
+	             */
+				state[0] = b0;
+				state[1] = b1;
+				state[2] = b2;
+				state[3] = b3;
+			}
+
+		}
+
+		private sealed class Threefish512Cipher
+			: ThreefishCipher
+		{
+			/**
+	         * Mix rotation constants defined in Skein 1.3 specification
+	         */
+			private const int ROTATION_0_0 = 46, ROTATION_0_1 = 36, ROTATION_0_2 = 19, ROTATION_0_3 = 37;
+			private const int ROTATION_1_0 = 33, ROTATION_1_1 = 27, ROTATION_1_2 = 14, ROTATION_1_3 = 42;
+			private const int ROTATION_2_0 = 17, ROTATION_2_1 = 49, ROTATION_2_2 = 36, ROTATION_2_3 = 39;
+			private const int ROTATION_3_0 = 44, ROTATION_3_1 = 9, ROTATION_3_2 = 54, ROTATION_3_3 = 56;
+
+			private const int ROTATION_4_0 = 39, ROTATION_4_1 = 30, ROTATION_4_2 = 34, ROTATION_4_3 = 24;
+			private const int ROTATION_5_0 = 13, ROTATION_5_1 = 50, ROTATION_5_2 = 10, ROTATION_5_3 = 17;
+			private const int ROTATION_6_0 = 25, ROTATION_6_1 = 29, ROTATION_6_2 = 39, ROTATION_6_3 = 43;
+			private const int ROTATION_7_0 = 8, ROTATION_7_1 = 35, ROTATION_7_2 = 56, ROTATION_7_3 = 22;
+
+			internal Threefish512Cipher(ulong[] kw, ulong[] t)
+				: base(kw, t)
+			{
+			}
+
+			internal override void EncryptBlock(ulong[] block, ulong[] outWords)
+			{
+				ulong[] kw = this.kw;
+				ulong[] t = this.t;
+				int[] mod9 = MOD9;
+				int[] mod3 = MOD3;
+
+				/* Help the JIT avoid index bounds checks */
+				if (kw.Length != 17)
+				{
+					throw new ArgumentException();
+				}
+				if (t.Length != 5)
+				{
+					throw new ArgumentException();
+				}
+
+				/*
+	             * Read 8 words of plaintext data, not using arrays for cipher state
+	             */
+				ulong b0 = block[0];
+				ulong b1 = block[1];
+				ulong b2 = block[2];
+				ulong b3 = block[3];
+				ulong b4 = block[4];
+				ulong b5 = block[5];
+				ulong b6 = block[6];
+				ulong b7 = block[7];
+
+				/*
+	             * First subkey injection.
+	             */
+				b0 += kw[0];
+				b1 += kw[1];
+				b2 += kw[2];
+				b3 += kw[3];
+				b4 += kw[4];
+				b5 += kw[5] + t[0];
+				b6 += kw[6] + t[1];
+				b7 += kw[7];
+
+				/*
+	             * Rounds loop, unrolled to 8 rounds per iteration.
+	             * 
+	             * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+	             * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+	             * index/lookup).
+	             * 
+	             * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+	             * inlining constant rotation values (avoiding array index/lookup).
+	             */
+
+				for (int d = 1; d < (ROUNDS_512 / 4); d += 2)
+				{
+					int dm9 = mod9[d];
+					int dm3 = mod3[d];
+
+					/*
+	                 * 4 rounds of mix and permute.
+	                 * 
+	                 * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+	                 * operations in each 4 round block.
+	                 */
+					b1 = RotlXor(b1, ROTATION_0_0, b0 += b1);
+					b3 = RotlXor(b3, ROTATION_0_1, b2 += b3);
+					b5 = RotlXor(b5, ROTATION_0_2, b4 += b5);
+					b7 = RotlXor(b7, ROTATION_0_3, b6 += b7);
+
+					b1 = RotlXor(b1, ROTATION_1_0, b2 += b1);
+					b7 = RotlXor(b7, ROTATION_1_1, b4 += b7);
+					b5 = RotlXor(b5, ROTATION_1_2, b6 += b5);
+					b3 = RotlXor(b3, ROTATION_1_3, b0 += b3);
+
+					b1 = RotlXor(b1, ROTATION_2_0, b4 += b1);
+					b3 = RotlXor(b3, ROTATION_2_1, b6 += b3);
+					b5 = RotlXor(b5, ROTATION_2_2, b0 += b5);
+					b7 = RotlXor(b7, ROTATION_2_3, b2 += b7);
+
+					b1 = RotlXor(b1, ROTATION_3_0, b6 += b1);
+					b7 = RotlXor(b7, ROTATION_3_1, b0 += b7);
+					b5 = RotlXor(b5, ROTATION_3_2, b2 += b5);
+					b3 = RotlXor(b3, ROTATION_3_3, b4 += b3);
+
+					/*
+	                 * Subkey injection for first 4 rounds.
+	                 */
+					b0 += kw[dm9];
+					b1 += kw[dm9 + 1];
+					b2 += kw[dm9 + 2];
+					b3 += kw[dm9 + 3];
+					b4 += kw[dm9 + 4];
+					b5 += kw[dm9 + 5] + t[dm3];
+					b6 += kw[dm9 + 6] + t[dm3 + 1];
+					b7 += kw[dm9 + 7] + (uint)d;
+
+					/*
+	                 * 4 more rounds of mix/permute
+	                 */
+					b1 = RotlXor(b1, ROTATION_4_0, b0 += b1);
+					b3 = RotlXor(b3, ROTATION_4_1, b2 += b3);
+					b5 = RotlXor(b5, ROTATION_4_2, b4 += b5);
+					b7 = RotlXor(b7, ROTATION_4_3, b6 += b7);
+
+					b1 = RotlXor(b1, ROTATION_5_0, b2 += b1);
+					b7 = RotlXor(b7, ROTATION_5_1, b4 += b7);
+					b5 = RotlXor(b5, ROTATION_5_2, b6 += b5);
+					b3 = RotlXor(b3, ROTATION_5_3, b0 += b3);
+
+					b1 = RotlXor(b1, ROTATION_6_0, b4 += b1);
+					b3 = RotlXor(b3, ROTATION_6_1, b6 += b3);
+					b5 = RotlXor(b5, ROTATION_6_2, b0 += b5);
+					b7 = RotlXor(b7, ROTATION_6_3, b2 += b7);
+
+					b1 = RotlXor(b1, ROTATION_7_0, b6 += b1);
+					b7 = RotlXor(b7, ROTATION_7_1, b0 += b7);
+					b5 = RotlXor(b5, ROTATION_7_2, b2 += b5);
+					b3 = RotlXor(b3, ROTATION_7_3, b4 += b3);
+
+					/*
+	                 * Subkey injection for next 4 rounds.
+	                 */
+					b0 += kw[dm9 + 1];
+					b1 += kw[dm9 + 2];
+					b2 += kw[dm9 + 3];
+					b3 += kw[dm9 + 4];
+					b4 += kw[dm9 + 5];
+					b5 += kw[dm9 + 6] + t[dm3 + 1];
+					b6 += kw[dm9 + 7] + t[dm3 + 2];
+					b7 += kw[dm9 + 8] + (uint)d + 1;
+				}
+
+				/*
+	             * Output cipher state.
+	             */
+				outWords[0] = b0;
+				outWords[1] = b1;
+				outWords[2] = b2;
+				outWords[3] = b3;
+				outWords[4] = b4;
+				outWords[5] = b5;
+				outWords[6] = b6;
+				outWords[7] = b7;
+			}
+
+			internal override void DecryptBlock(ulong[] block, ulong[] state)
+			{
+				ulong[] kw = this.kw;
+				ulong[] t = this.t;
+				int[] mod9 = MOD9;
+				int[] mod3 = MOD3;
+
+				/* Help the JIT avoid index bounds checks */
+				if (kw.Length != 17)
+				{
+					throw new ArgumentException();
+				}
+				if (t.Length != 5)
+				{
+					throw new ArgumentException();
+				}
+
+				ulong b0 = block[0];
+				ulong b1 = block[1];
+				ulong b2 = block[2];
+				ulong b3 = block[3];
+				ulong b4 = block[4];
+				ulong b5 = block[5];
+				ulong b6 = block[6];
+				ulong b7 = block[7];
+
+				for (int d = (ROUNDS_512 / 4) - 1; d >= 1; d -= 2)
+				{
+					int dm9 = mod9[d];
+					int dm3 = mod3[d];
+
+					/* Reverse key injection for second 4 rounds */
+					b0 -= kw[dm9 + 1];
+					b1 -= kw[dm9 + 2];
+					b2 -= kw[dm9 + 3];
+					b3 -= kw[dm9 + 4];
+					b4 -= kw[dm9 + 5];
+					b5 -= kw[dm9 + 6] + t[dm3 + 1];
+					b6 -= kw[dm9 + 7] + t[dm3 + 2];
+					b7 -= kw[dm9 + 8] + (uint)d + 1;
+
+					/* Reverse second 4 mix/permute rounds */
+
+					b1 = XorRotr(b1, ROTATION_7_0, b6);
+					b6 -= b1;
+					b7 = XorRotr(b7, ROTATION_7_1, b0);
+					b0 -= b7;
+					b5 = XorRotr(b5, ROTATION_7_2, b2);
+					b2 -= b5;
+					b3 = XorRotr(b3, ROTATION_7_3, b4);
+					b4 -= b3;
+
+					b1 = XorRotr(b1, ROTATION_6_0, b4);
+					b4 -= b1;
+					b3 = XorRotr(b3, ROTATION_6_1, b6);
+					b6 -= b3;
+					b5 = XorRotr(b5, ROTATION_6_2, b0);
+					b0 -= b5;
+					b7 = XorRotr(b7, ROTATION_6_3, b2);
+					b2 -= b7;
+
+					b1 = XorRotr(b1, ROTATION_5_0, b2);
+					b2 -= b1;
+					b7 = XorRotr(b7, ROTATION_5_1, b4);
+					b4 -= b7;
+					b5 = XorRotr(b5, ROTATION_5_2, b6);
+					b6 -= b5;
+					b3 = XorRotr(b3, ROTATION_5_3, b0);
+					b0 -= b3;
+
+					b1 = XorRotr(b1, ROTATION_4_0, b0);
+					b0 -= b1;
+					b3 = XorRotr(b3, ROTATION_4_1, b2);
+					b2 -= b3;
+					b5 = XorRotr(b5, ROTATION_4_2, b4);
+					b4 -= b5;
+					b7 = XorRotr(b7, ROTATION_4_3, b6);
+					b6 -= b7;
+
+					/* Reverse key injection for first 4 rounds */
+					b0 -= kw[dm9];
+					b1 -= kw[dm9 + 1];
+					b2 -= kw[dm9 + 2];
+					b3 -= kw[dm9 + 3];
+					b4 -= kw[dm9 + 4];
+					b5 -= kw[dm9 + 5] + t[dm3];
+					b6 -= kw[dm9 + 6] + t[dm3 + 1];
+					b7 -= kw[dm9 + 7] + (uint)d;
+
+					/* Reverse first 4 mix/permute rounds */
+					b1 = XorRotr(b1, ROTATION_3_0, b6);
+					b6 -= b1;
+					b7 = XorRotr(b7, ROTATION_3_1, b0);
+					b0 -= b7;
+					b5 = XorRotr(b5, ROTATION_3_2, b2);
+					b2 -= b5;
+					b3 = XorRotr(b3, ROTATION_3_3, b4);
+					b4 -= b3;
+
+					b1 = XorRotr(b1, ROTATION_2_0, b4);
+					b4 -= b1;
+					b3 = XorRotr(b3, ROTATION_2_1, b6);
+					b6 -= b3;
+					b5 = XorRotr(b5, ROTATION_2_2, b0);
+					b0 -= b5;
+					b7 = XorRotr(b7, ROTATION_2_3, b2);
+					b2 -= b7;
+
+					b1 = XorRotr(b1, ROTATION_1_0, b2);
+					b2 -= b1;
+					b7 = XorRotr(b7, ROTATION_1_1, b4);
+					b4 -= b7;
+					b5 = XorRotr(b5, ROTATION_1_2, b6);
+					b6 -= b5;
+					b3 = XorRotr(b3, ROTATION_1_3, b0);
+					b0 -= b3;
+
+					b1 = XorRotr(b1, ROTATION_0_0, b0);
+					b0 -= b1;
+					b3 = XorRotr(b3, ROTATION_0_1, b2);
+					b2 -= b3;
+					b5 = XorRotr(b5, ROTATION_0_2, b4);
+					b4 -= b5;
+					b7 = XorRotr(b7, ROTATION_0_3, b6);
+					b6 -= b7;
+				}
+
+				/*
+	             * First subkey uninjection.
+	             */
+				b0 -= kw[0];
+				b1 -= kw[1];
+				b2 -= kw[2];
+				b3 -= kw[3];
+				b4 -= kw[4];
+				b5 -= kw[5] + t[0];
+				b6 -= kw[6] + t[1];
+				b7 -= kw[7];
+
+				/*
+	             * Output cipher state.
+	             */
+				state[0] = b0;
+				state[1] = b1;
+				state[2] = b2;
+				state[3] = b3;
+				state[4] = b4;
+				state[5] = b5;
+				state[6] = b6;
+				state[7] = b7;
+			}
+		}
+
+		private sealed class Threefish1024Cipher
+			: ThreefishCipher
+		{
+			/**
+	         * Mix rotation constants defined in Skein 1.3 specification
+	         */
+			private const int ROTATION_0_0 = 24, ROTATION_0_1 = 13, ROTATION_0_2 = 8, ROTATION_0_3 = 47;
+			private const int ROTATION_0_4 = 8, ROTATION_0_5 = 17, ROTATION_0_6 = 22, ROTATION_0_7 = 37;
+			private const int ROTATION_1_0 = 38, ROTATION_1_1 = 19, ROTATION_1_2 = 10, ROTATION_1_3 = 55;
+			private const int ROTATION_1_4 = 49, ROTATION_1_5 = 18, ROTATION_1_6 = 23, ROTATION_1_7 = 52;
+			private const int ROTATION_2_0 = 33, ROTATION_2_1 = 4, ROTATION_2_2 = 51, ROTATION_2_3 = 13;
+			private const int ROTATION_2_4 = 34, ROTATION_2_5 = 41, ROTATION_2_6 = 59, ROTATION_2_7 = 17;
+			private const int ROTATION_3_0 = 5, ROTATION_3_1 = 20, ROTATION_3_2 = 48, ROTATION_3_3 = 41;
+			private const int ROTATION_3_4 = 47, ROTATION_3_5 = 28, ROTATION_3_6 = 16, ROTATION_3_7 = 25;
+
+			private const int ROTATION_4_0 = 41, ROTATION_4_1 = 9, ROTATION_4_2 = 37, ROTATION_4_3 = 31;
+			private const int ROTATION_4_4 = 12, ROTATION_4_5 = 47, ROTATION_4_6 = 44, ROTATION_4_7 = 30;
+			private const int ROTATION_5_0 = 16, ROTATION_5_1 = 34, ROTATION_5_2 = 56, ROTATION_5_3 = 51;
+			private const int ROTATION_5_4 = 4, ROTATION_5_5 = 53, ROTATION_5_6 = 42, ROTATION_5_7 = 41;
+			private const int ROTATION_6_0 = 31, ROTATION_6_1 = 44, ROTATION_6_2 = 47, ROTATION_6_3 = 46;
+			private const int ROTATION_6_4 = 19, ROTATION_6_5 = 42, ROTATION_6_6 = 44, ROTATION_6_7 = 25;
+			private const int ROTATION_7_0 = 9, ROTATION_7_1 = 48, ROTATION_7_2 = 35, ROTATION_7_3 = 52;
+			private const int ROTATION_7_4 = 23, ROTATION_7_5 = 31, ROTATION_7_6 = 37, ROTATION_7_7 = 20;
+
+			public Threefish1024Cipher(ulong[] kw, ulong[] t)
+				: base(kw, t)
+			{
+			}
+
+			internal override void EncryptBlock(ulong[] block, ulong[] outWords)
+			{
+				ulong[] kw = this.kw;
+				ulong[] t = this.t;
+				int[] mod17 = MOD17;
+				int[] mod3 = MOD3;
+
+				/* Help the JIT avoid index bounds checks */
+				if (kw.Length != 33)
+				{
+					throw new ArgumentException();
+				}
+				if (t.Length != 5)
+				{
+					throw new ArgumentException();
+				}
+
+				/*
+	             * Read 16 words of plaintext data, not using arrays for cipher state
+	             */
+				ulong b0 = block[0];
+				ulong b1 = block[1];
+				ulong b2 = block[2];
+				ulong b3 = block[3];
+				ulong b4 = block[4];
+				ulong b5 = block[5];
+				ulong b6 = block[6];
+				ulong b7 = block[7];
+				ulong b8 = block[8];
+				ulong b9 = block[9];
+				ulong b10 = block[10];
+				ulong b11 = block[11];
+				ulong b12 = block[12];
+				ulong b13 = block[13];
+				ulong b14 = block[14];
+				ulong b15 = block[15];
+
+				/*
+	             * First subkey injection.
+	             */
+				b0 += kw[0];
+				b1 += kw[1];
+				b2 += kw[2];
+				b3 += kw[3];
+				b4 += kw[4];
+				b5 += kw[5];
+				b6 += kw[6];
+				b7 += kw[7];
+				b8 += kw[8];
+				b9 += kw[9];
+				b10 += kw[10];
+				b11 += kw[11];
+				b12 += kw[12];
+				b13 += kw[13] + t[0];
+				b14 += kw[14] + t[1];
+				b15 += kw[15];
+
+				/*
+	             * Rounds loop, unrolled to 8 rounds per iteration.
+	             * 
+	             * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+	             * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+	             * index/lookup).
+	             * 
+	             * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+	             * inlining constant rotation values (avoiding array index/lookup).
+	             */
+
+				for (int d = 1; d < (ROUNDS_1024 / 4); d += 2)
+				{
+					int dm17 = mod17[d];
+					int dm3 = mod3[d];
+
+					/*
+	                 * 4 rounds of mix and permute.
+	                 * 
+	                 * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+	                 * operations in each 4 round block.
+	                 */
+					b1 = RotlXor(b1, ROTATION_0_0, b0 += b1);
+					b3 = RotlXor(b3, ROTATION_0_1, b2 += b3);
+					b5 = RotlXor(b5, ROTATION_0_2, b4 += b5);
+					b7 = RotlXor(b7, ROTATION_0_3, b6 += b7);
+					b9 = RotlXor(b9, ROTATION_0_4, b8 += b9);
+					b11 = RotlXor(b11, ROTATION_0_5, b10 += b11);
+					b13 = RotlXor(b13, ROTATION_0_6, b12 += b13);
+					b15 = RotlXor(b15, ROTATION_0_7, b14 += b15);
+
+					b9 = RotlXor(b9, ROTATION_1_0, b0 += b9);
+					b13 = RotlXor(b13, ROTATION_1_1, b2 += b13);
+					b11 = RotlXor(b11, ROTATION_1_2, b6 += b11);
+					b15 = RotlXor(b15, ROTATION_1_3, b4 += b15);
+					b7 = RotlXor(b7, ROTATION_1_4, b10 += b7);
+					b3 = RotlXor(b3, ROTATION_1_5, b12 += b3);
+					b5 = RotlXor(b5, ROTATION_1_6, b14 += b5);
+					b1 = RotlXor(b1, ROTATION_1_7, b8 += b1);
+
+					b7 = RotlXor(b7, ROTATION_2_0, b0 += b7);
+					b5 = RotlXor(b5, ROTATION_2_1, b2 += b5);
+					b3 = RotlXor(b3, ROTATION_2_2, b4 += b3);
+					b1 = RotlXor(b1, ROTATION_2_3, b6 += b1);
+					b15 = RotlXor(b15, ROTATION_2_4, b12 += b15);
+					b13 = RotlXor(b13, ROTATION_2_5, b14 += b13);
+					b11 = RotlXor(b11, ROTATION_2_6, b8 += b11);
+					b9 = RotlXor(b9, ROTATION_2_7, b10 += b9);
+
+					b15 = RotlXor(b15, ROTATION_3_0, b0 += b15);
+					b11 = RotlXor(b11, ROTATION_3_1, b2 += b11);
+					b13 = RotlXor(b13, ROTATION_3_2, b6 += b13);
+					b9 = RotlXor(b9, ROTATION_3_3, b4 += b9);
+					b1 = RotlXor(b1, ROTATION_3_4, b14 += b1);
+					b5 = RotlXor(b5, ROTATION_3_5, b8 += b5);
+					b3 = RotlXor(b3, ROTATION_3_6, b10 += b3);
+					b7 = RotlXor(b7, ROTATION_3_7, b12 += b7);
+
+					/*
+	                 * Subkey injection for first 4 rounds.
+	                 */
+					b0 += kw[dm17];
+					b1 += kw[dm17 + 1];
+					b2 += kw[dm17 + 2];
+					b3 += kw[dm17 + 3];
+					b4 += kw[dm17 + 4];
+					b5 += kw[dm17 + 5];
+					b6 += kw[dm17 + 6];
+					b7 += kw[dm17 + 7];
+					b8 += kw[dm17 + 8];
+					b9 += kw[dm17 + 9];
+					b10 += kw[dm17 + 10];
+					b11 += kw[dm17 + 11];
+					b12 += kw[dm17 + 12];
+					b13 += kw[dm17 + 13] + t[dm3];
+					b14 += kw[dm17 + 14] + t[dm3 + 1];
+					b15 += kw[dm17 + 15] + (uint)d;
+
+					/*
+	                 * 4 more rounds of mix/permute
+	                 */
+					b1 = RotlXor(b1, ROTATION_4_0, b0 += b1);
+					b3 = RotlXor(b3, ROTATION_4_1, b2 += b3);
+					b5 = RotlXor(b5, ROTATION_4_2, b4 += b5);
+					b7 = RotlXor(b7, ROTATION_4_3, b6 += b7);
+					b9 = RotlXor(b9, ROTATION_4_4, b8 += b9);
+					b11 = RotlXor(b11, ROTATION_4_5, b10 += b11);
+					b13 = RotlXor(b13, ROTATION_4_6, b12 += b13);
+					b15 = RotlXor(b15, ROTATION_4_7, b14 += b15);
+
+					b9 = RotlXor(b9, ROTATION_5_0, b0 += b9);
+					b13 = RotlXor(b13, ROTATION_5_1, b2 += b13);
+					b11 = RotlXor(b11, ROTATION_5_2, b6 += b11);
+					b15 = RotlXor(b15, ROTATION_5_3, b4 += b15);
+					b7 = RotlXor(b7, ROTATION_5_4, b10 += b7);
+					b3 = RotlXor(b3, ROTATION_5_5, b12 += b3);
+					b5 = RotlXor(b5, ROTATION_5_6, b14 += b5);
+					b1 = RotlXor(b1, ROTATION_5_7, b8 += b1);
+
+					b7 = RotlXor(b7, ROTATION_6_0, b0 += b7);
+					b5 = RotlXor(b5, ROTATION_6_1, b2 += b5);
+					b3 = RotlXor(b3, ROTATION_6_2, b4 += b3);
+					b1 = RotlXor(b1, ROTATION_6_3, b6 += b1);
+					b15 = RotlXor(b15, ROTATION_6_4, b12 += b15);
+					b13 = RotlXor(b13, ROTATION_6_5, b14 += b13);
+					b11 = RotlXor(b11, ROTATION_6_6, b8 += b11);
+					b9 = RotlXor(b9, ROTATION_6_7, b10 += b9);
+
+					b15 = RotlXor(b15, ROTATION_7_0, b0 += b15);
+					b11 = RotlXor(b11, ROTATION_7_1, b2 += b11);
+					b13 = RotlXor(b13, ROTATION_7_2, b6 += b13);
+					b9 = RotlXor(b9, ROTATION_7_3, b4 += b9);
+					b1 = RotlXor(b1, ROTATION_7_4, b14 += b1);
+					b5 = RotlXor(b5, ROTATION_7_5, b8 += b5);
+					b3 = RotlXor(b3, ROTATION_7_6, b10 += b3);
+					b7 = RotlXor(b7, ROTATION_7_7, b12 += b7);
+
+					/*
+	                 * Subkey injection for next 4 rounds.
+	                 */
+					b0 += kw[dm17 + 1];
+					b1 += kw[dm17 + 2];
+					b2 += kw[dm17 + 3];
+					b3 += kw[dm17 + 4];
+					b4 += kw[dm17 + 5];
+					b5 += kw[dm17 + 6];
+					b6 += kw[dm17 + 7];
+					b7 += kw[dm17 + 8];
+					b8 += kw[dm17 + 9];
+					b9 += kw[dm17 + 10];
+					b10 += kw[dm17 + 11];
+					b11 += kw[dm17 + 12];
+					b12 += kw[dm17 + 13];
+					b13 += kw[dm17 + 14] + t[dm3 + 1];
+					b14 += kw[dm17 + 15] + t[dm3 + 2];
+					b15 += kw[dm17 + 16] + (uint)d + 1;
+
+				}
+
+				/*
+	             * Output cipher state.
+	             */
+				outWords[0] = b0;
+				outWords[1] = b1;
+				outWords[2] = b2;
+				outWords[3] = b3;
+				outWords[4] = b4;
+				outWords[5] = b5;
+				outWords[6] = b6;
+				outWords[7] = b7;
+				outWords[8] = b8;
+				outWords[9] = b9;
+				outWords[10] = b10;
+				outWords[11] = b11;
+				outWords[12] = b12;
+				outWords[13] = b13;
+				outWords[14] = b14;
+				outWords[15] = b15;
+			}
+
+			internal override void DecryptBlock(ulong[] block, ulong[] state)
+			{
+				ulong[] kw = this.kw;
+				ulong[] t = this.t;
+				int[] mod17 = MOD17;
+				int[] mod3 = MOD3;
+
+				/* Help the JIT avoid index bounds checks */
+				if (kw.Length != 33)
+				{
+					throw new ArgumentException();
+				}
+				if (t.Length != 5)
+				{
+					throw new ArgumentException();
+				}
+
+				ulong b0 = block[0];
+				ulong b1 = block[1];
+				ulong b2 = block[2];
+				ulong b3 = block[3];
+				ulong b4 = block[4];
+				ulong b5 = block[5];
+				ulong b6 = block[6];
+				ulong b7 = block[7];
+				ulong b8 = block[8];
+				ulong b9 = block[9];
+				ulong b10 = block[10];
+				ulong b11 = block[11];
+				ulong b12 = block[12];
+				ulong b13 = block[13];
+				ulong b14 = block[14];
+				ulong b15 = block[15];
+
+				for (int d = (ROUNDS_1024 / 4) - 1; d >= 1; d -= 2)
+				{
+					int dm17 = mod17[d];
+					int dm3 = mod3[d];
+
+					/* Reverse key injection for second 4 rounds */
+					b0 -= kw[dm17 + 1];
+					b1 -= kw[dm17 + 2];
+					b2 -= kw[dm17 + 3];
+					b3 -= kw[dm17 + 4];
+					b4 -= kw[dm17 + 5];
+					b5 -= kw[dm17 + 6];
+					b6 -= kw[dm17 + 7];
+					b7 -= kw[dm17 + 8];
+					b8 -= kw[dm17 + 9];
+					b9 -= kw[dm17 + 10];
+					b10 -= kw[dm17 + 11];
+					b11 -= kw[dm17 + 12];
+					b12 -= kw[dm17 + 13];
+					b13 -= kw[dm17 + 14] + t[dm3 + 1];
+					b14 -= kw[dm17 + 15] + t[dm3 + 2];
+					b15 -= kw[dm17 + 16] + (uint)d + 1;
+
+					/* Reverse second 4 mix/permute rounds */
+					b15 = XorRotr(b15, ROTATION_7_0, b0);
+					b0 -= b15;
+					b11 = XorRotr(b11, ROTATION_7_1, b2);
+					b2 -= b11;
+					b13 = XorRotr(b13, ROTATION_7_2, b6);
+					b6 -= b13;
+					b9 = XorRotr(b9, ROTATION_7_3, b4);
+					b4 -= b9;
+					b1 = XorRotr(b1, ROTATION_7_4, b14);
+					b14 -= b1;
+					b5 = XorRotr(b5, ROTATION_7_5, b8);
+					b8 -= b5;
+					b3 = XorRotr(b3, ROTATION_7_6, b10);
+					b10 -= b3;
+					b7 = XorRotr(b7, ROTATION_7_7, b12);
+					b12 -= b7;
+
+					b7 = XorRotr(b7, ROTATION_6_0, b0);
+					b0 -= b7;
+					b5 = XorRotr(b5, ROTATION_6_1, b2);
+					b2 -= b5;
+					b3 = XorRotr(b3, ROTATION_6_2, b4);
+					b4 -= b3;
+					b1 = XorRotr(b1, ROTATION_6_3, b6);
+					b6 -= b1;
+					b15 = XorRotr(b15, ROTATION_6_4, b12);
+					b12 -= b15;
+					b13 = XorRotr(b13, ROTATION_6_5, b14);
+					b14 -= b13;
+					b11 = XorRotr(b11, ROTATION_6_6, b8);
+					b8 -= b11;
+					b9 = XorRotr(b9, ROTATION_6_7, b10);
+					b10 -= b9;
+
+					b9 = XorRotr(b9, ROTATION_5_0, b0);
+					b0 -= b9;
+					b13 = XorRotr(b13, ROTATION_5_1, b2);
+					b2 -= b13;
+					b11 = XorRotr(b11, ROTATION_5_2, b6);
+					b6 -= b11;
+					b15 = XorRotr(b15, ROTATION_5_3, b4);
+					b4 -= b15;
+					b7 = XorRotr(b7, ROTATION_5_4, b10);
+					b10 -= b7;
+					b3 = XorRotr(b3, ROTATION_5_5, b12);
+					b12 -= b3;
+					b5 = XorRotr(b5, ROTATION_5_6, b14);
+					b14 -= b5;
+					b1 = XorRotr(b1, ROTATION_5_7, b8);
+					b8 -= b1;
+
+					b1 = XorRotr(b1, ROTATION_4_0, b0);
+					b0 -= b1;
+					b3 = XorRotr(b3, ROTATION_4_1, b2);
+					b2 -= b3;
+					b5 = XorRotr(b5, ROTATION_4_2, b4);
+					b4 -= b5;
+					b7 = XorRotr(b7, ROTATION_4_3, b6);
+					b6 -= b7;
+					b9 = XorRotr(b9, ROTATION_4_4, b8);
+					b8 -= b9;
+					b11 = XorRotr(b11, ROTATION_4_5, b10);
+					b10 -= b11;
+					b13 = XorRotr(b13, ROTATION_4_6, b12);
+					b12 -= b13;
+					b15 = XorRotr(b15, ROTATION_4_7, b14);
+					b14 -= b15;
+
+					/* Reverse key injection for first 4 rounds */
+					b0 -= kw[dm17];
+					b1 -= kw[dm17 + 1];
+					b2 -= kw[dm17 + 2];
+					b3 -= kw[dm17 + 3];
+					b4 -= kw[dm17 + 4];
+					b5 -= kw[dm17 + 5];
+					b6 -= kw[dm17 + 6];
+					b7 -= kw[dm17 + 7];
+					b8 -= kw[dm17 + 8];
+					b9 -= kw[dm17 + 9];
+					b10 -= kw[dm17 + 10];
+					b11 -= kw[dm17 + 11];
+					b12 -= kw[dm17 + 12];
+					b13 -= kw[dm17 + 13] + t[dm3];
+					b14 -= kw[dm17 + 14] + t[dm3 + 1];
+					b15 -= kw[dm17 + 15] + (uint)d;
+
+					/* Reverse first 4 mix/permute rounds */
+					b15 = XorRotr(b15, ROTATION_3_0, b0);
+					b0 -= b15;
+					b11 = XorRotr(b11, ROTATION_3_1, b2);
+					b2 -= b11;
+					b13 = XorRotr(b13, ROTATION_3_2, b6);
+					b6 -= b13;
+					b9 = XorRotr(b9, ROTATION_3_3, b4);
+					b4 -= b9;
+					b1 = XorRotr(b1, ROTATION_3_4, b14);
+					b14 -= b1;
+					b5 = XorRotr(b5, ROTATION_3_5, b8);
+					b8 -= b5;
+					b3 = XorRotr(b3, ROTATION_3_6, b10);
+					b10 -= b3;
+					b7 = XorRotr(b7, ROTATION_3_7, b12);
+					b12 -= b7;
+
+					b7 = XorRotr(b7, ROTATION_2_0, b0);
+					b0 -= b7;
+					b5 = XorRotr(b5, ROTATION_2_1, b2);
+					b2 -= b5;
+					b3 = XorRotr(b3, ROTATION_2_2, b4);
+					b4 -= b3;
+					b1 = XorRotr(b1, ROTATION_2_3, b6);
+					b6 -= b1;
+					b15 = XorRotr(b15, ROTATION_2_4, b12);
+					b12 -= b15;
+					b13 = XorRotr(b13, ROTATION_2_5, b14);
+					b14 -= b13;
+					b11 = XorRotr(b11, ROTATION_2_6, b8);
+					b8 -= b11;
+					b9 = XorRotr(b9, ROTATION_2_7, b10);
+					b10 -= b9;
+
+					b9 = XorRotr(b9, ROTATION_1_0, b0);
+					b0 -= b9;
+					b13 = XorRotr(b13, ROTATION_1_1, b2);
+					b2 -= b13;
+					b11 = XorRotr(b11, ROTATION_1_2, b6);
+					b6 -= b11;
+					b15 = XorRotr(b15, ROTATION_1_3, b4);
+					b4 -= b15;
+					b7 = XorRotr(b7, ROTATION_1_4, b10);
+					b10 -= b7;
+					b3 = XorRotr(b3, ROTATION_1_5, b12);
+					b12 -= b3;
+					b5 = XorRotr(b5, ROTATION_1_6, b14);
+					b14 -= b5;
+					b1 = XorRotr(b1, ROTATION_1_7, b8);
+					b8 -= b1;
+
+					b1 = XorRotr(b1, ROTATION_0_0, b0);
+					b0 -= b1;
+					b3 = XorRotr(b3, ROTATION_0_1, b2);
+					b2 -= b3;
+					b5 = XorRotr(b5, ROTATION_0_2, b4);
+					b4 -= b5;
+					b7 = XorRotr(b7, ROTATION_0_3, b6);
+					b6 -= b7;
+					b9 = XorRotr(b9, ROTATION_0_4, b8);
+					b8 -= b9;
+					b11 = XorRotr(b11, ROTATION_0_5, b10);
+					b10 -= b11;
+					b13 = XorRotr(b13, ROTATION_0_6, b12);
+					b12 -= b13;
+					b15 = XorRotr(b15, ROTATION_0_7, b14);
+					b14 -= b15;
+				}
+
+				/*
+	             * First subkey uninjection.
+	             */
+				b0 -= kw[0];
+				b1 -= kw[1];
+				b2 -= kw[2];
+				b3 -= kw[3];
+				b4 -= kw[4];
+				b5 -= kw[5];
+				b6 -= kw[6];
+				b7 -= kw[7];
+				b8 -= kw[8];
+				b9 -= kw[9];
+				b10 -= kw[10];
+				b11 -= kw[11];
+				b12 -= kw[12];
+				b13 -= kw[13] + t[0];
+				b14 -= kw[14] + t[1];
+				b15 -= kw[15];
+
+				/*
+	             * Output cipher state.
+	             */
+				state[0] = b0;
+				state[1] = b1;
+				state[2] = b2;
+				state[3] = b3;
+				state[4] = b4;
+				state[5] = b5;
+				state[6] = b6;
+				state[7] = b7;
+				state[8] = b8;
+				state[9] = b9;
+				state[10] = b10;
+				state[11] = b11;
+				state[12] = b12;
+				state[13] = b13;
+				state[14] = b14;
+				state[15] = b15;
+			}
+
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/crypto/src/crypto/macs/HMac.cs b/crypto/src/crypto/macs/HMac.cs
index 7ae1e02b9..460f3c5a0 100644
--- a/crypto/src/crypto/macs/HMac.cs
+++ b/crypto/src/crypto/macs/HMac.cs
@@ -3,6 +3,7 @@ using System.Collections;
 
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Macs
 {
@@ -20,6 +21,8 @@ namespace Org.BouncyCastle.Crypto.Macs
         private readonly IDigest digest;
         private readonly int digestSize;
         private readonly int blockLength;
+		private IMemoable ipadState;
+		private IMemoable opadState;
 
 		private readonly byte[] inputPad;
         private readonly byte[] outputBuf;
@@ -68,8 +71,19 @@ namespace Org.BouncyCastle.Crypto.Macs
 			XorPad(inputPad, blockLength, IPAD);
             XorPad(outputBuf, blockLength, OPAD);
 
-            // Initialise the digest
+			if (digest is IMemoable)
+			{
+				opadState = ((IMemoable)digest).Copy();
+
+				((IDigest)opadState).BlockUpdate(outputBuf, 0, blockLength);
+			}
+
 			digest.BlockUpdate(inputPad, 0, inputPad.Length);
+
+			if (digest is IMemoable)
+			{
+				ipadState = ((IMemoable)digest).Copy();
+			}
         }
 
         public virtual int GetMacSize()
@@ -90,13 +104,29 @@ namespace Org.BouncyCastle.Crypto.Macs
         public virtual int DoFinal(byte[] output, int outOff)
         {
             digest.DoFinal(outputBuf, blockLength);
-            digest.BlockUpdate(outputBuf, 0, outputBuf.Length);
-            int len = digest.DoFinal(output, outOff);
 
-            Array.Clear(outputBuf, blockLength, digestSize);
-
-			// Initialise the digest
-            digest.BlockUpdate(inputPad, 0, inputPad.Length);
+			if (opadState != null)
+			{
+				((IMemoable)digest).Reset(opadState);
+				digest.BlockUpdate(outputBuf, blockLength, digest.GetDigestSize());
+			}
+			else
+			{
+				digest.BlockUpdate(outputBuf, 0, outputBuf.Length);
+			}
+
+			int len = digest.DoFinal(output, outOff);
+
+			Array.Clear(outputBuf, blockLength, digestSize);
+
+			if (ipadState != null)
+			{
+				((IMemoable)digest).Reset(ipadState);
+			}
+			else
+			{
+				digest.BlockUpdate(inputPad, 0, inputPad.Length);
+			}
 
             return len;
         }
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/crypto/parameters/TweakableBlockCipherParameters.cs b/crypto/src/crypto/parameters/TweakableBlockCipherParameters.cs
new file mode 100644
index 000000000..f75726600
--- /dev/null
+++ b/crypto/src/crypto/parameters/TweakableBlockCipherParameters.cs
@@ -0,0 +1,40 @@
+using System;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+
+	/// <summary>
+	/// Parameters for tweakable block ciphers.
+	/// </summary>
+	public class TweakableBlockCipherParameters
+		: ICipherParameters
+	{
+		private readonly byte[] tweak;
+		private readonly KeyParameter key;
+
+		public TweakableBlockCipherParameters(KeyParameter key, byte[] tweak)
+		{
+			this.key = key;
+			this.tweak = Arrays.Clone(tweak);
+		}
+
+		/// <summary>
+		/// Gets the key.
+		/// </summary>
+		/// <value>the key to use, or <code>null</code> to use the current key.</value>
+		public KeyParameter Key
+		{
+			get { return key; }
+		}
+
+		/// <summary>
+		/// Gets the tweak value.
+		/// </summary>
+		/// <value>The tweak to use, or <code>null</code> to use the current tweak.</value>
+		public byte[] Tweak
+		{
+			get { return tweak; }
+		}
+	}
+}
\ No newline at end of file
diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index 5eab42bd7..d90bbdd90 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -236,6 +236,30 @@ namespace Org.BouncyCastle.Utilities
             }
         }
 
+        [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 byte[] Copy(byte[] data, int off, int len)
         {
             byte[] result = new byte[len];
diff --git a/crypto/src/util/IMemoable.cs b/crypto/src/util/IMemoable.cs
new file mode 100644
index 000000000..befc10fbf
--- /dev/null
+++ b/crypto/src/util/IMemoable.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Org.BouncyCastle.Utilities
+{
+	public interface IMemoable
+	{
+		/// <summary>
+		/// Produce a copy of this object with its configuration and in its current state.
+		/// </summary>
+		/// <remarks>
+		/// The returned object may be used simply to store the state, or may be used as a similar object
+		/// starting from the copied state.
+		/// </remarks>
+		IMemoable Copy();
+
+		/// <summary>
+		/// Restore a copied object state into this object.
+		/// </summary>
+		/// <remarks>
+		/// Implementations of this method <em>should</em> try to avoid or minimise memory allocation to perform the reset.
+		/// </remarks>
+		/// <param name="other">an object originally {@link #copy() copied} from an object of the same type as this instance.</param>
+		/// <exception cref="ClassCastException">if the provided object is not of the correct type.</exception>
+		/// <exception cref="MemoableResetException">if the <b>other</b> parameter is in some other way invalid.</exception>
+		void Reset(IMemoable other);
+	}
+
+}
+
diff --git a/crypto/src/util/MemoableResetException.cs b/crypto/src/util/MemoableResetException.cs
new file mode 100644
index 000000000..d9542dab2
--- /dev/null
+++ b/crypto/src/util/MemoableResetException.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Org.BouncyCastle.Utilities
+{
+	/**
+	 * Exception to be thrown on a failure to reset an object implementing Memoable.
+	 * <p>
+	 * The exception extends ClassCastException to enable users to have a single handling case,
+	 * only introducing specific handling of this one if required.
+	 * </p>
+	 */
+	public class MemoableResetException
+		: InvalidCastException
+	{
+		/**
+	     * Basic Constructor.
+	     *
+	     * @param msg message to be associated with this exception.
+	     */
+		public MemoableResetException(string msg)
+			: base(msg)
+		{
+		}
+	}
+
+}
+