summary refs log tree commit diff
diff options
context:
space:
mode:
authorTim Whittington <bc@whittington.net.nz>2013-10-10 12:47:44 +1300
committerTim Whittington <bc@whittington.net.nz>2013-10-10 12:56:38 +1300
commit4e0d67b4ddcee08a49e8922159eebc6d11f2ae6d (patch)
tree4851e73d038fa4eea1429999820faf749ed17cbe
parentfixed line endings (diff)
downloadBouncyCastle.NET-ed25519-4e0d67b4ddcee08a49e8922159eebc6d11f2ae6d.tar.xz
Port reduced round Salsa20, registerised Salsa20 core, XSalsa20 and ChaCha from bc-java.
-rw-r--r--crypto/crypto.mdp4
-rw-r--r--crypto/src/crypto/engines/ChaChaEngine.cs191
-rw-r--r--crypto/src/crypto/engines/Salsa20Engine.cs264
-rw-r--r--crypto/src/crypto/engines/XSalsa20Engine.cs70
-rw-r--r--crypto/test/src/crypto/test/ChaChaTest.cs318
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs4
-rw-r--r--crypto/test/src/crypto/test/Salsa20Test.cs69
-rw-r--r--crypto/test/src/crypto/test/XSalsa20Test.cs183
8 files changed, 1005 insertions, 98 deletions
diff --git a/crypto/crypto.mdp b/crypto/crypto.mdp
index 8152a40a9..483491488 100644
--- a/crypto/crypto.mdp
+++ b/crypto/crypto.mdp
@@ -2297,6 +2297,10 @@
     <File subtype="Code" buildaction="Compile" name="src/bcpg/sig/RevocationReason.cs" />
     <File subtype="Code" buildaction="Compile" name="src/bcpg/sig/RevocationReasonTags.cs" />
     <File subtype="Code" buildaction="Compile" name="src/bcpg/sig/RevocationKeyTags.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/crypto/engines/ChaChaEngine.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/crypto/engines/XSalsa20Engine.cs" />
+    <File subtype="Code" buildaction="Compile" name="test/src/crypto/test/ChaChaTest.cs" />
+    <File subtype="Code" buildaction="Compile" name="test/src/crypto/test/XSalsa20Test.cs" />
   </Contents>
   <References>
     <ProjectReference type="Assembly" localcopy="True" refto="test/lib/nunit.core.dll" />
diff --git a/crypto/src/crypto/engines/ChaChaEngine.cs b/crypto/src/crypto/engines/ChaChaEngine.cs
new file mode 100644
index 000000000..0971b8035
--- /dev/null
+++ b/crypto/src/crypto/engines/ChaChaEngine.cs
@@ -0,0 +1,191 @@
+using System;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+	/**
+	 * Implementation of Daniel J. Bernstein's ChaCha stream cipher.
+	 */
+	[CLSCompliantAttribute(false)]
+	public class ChaChaEngine
+		: Salsa20Engine
+	{
+
+		/**
+     	* Creates a 20 rounds ChaCha engine.
+     	*/
+		public ChaChaEngine()
+		{
+		}
+
+		/**
+		 * Creates a ChaCha engine with a specific number of rounds.
+		 * @param rounds the number of rounds (must be an even number).
+		 */
+		public ChaChaEngine(int rounds)
+			: base(rounds)
+		{
+		}
+
+		public override string AlgorithmName
+		{
+			get { return "ChaCha" + rounds; }
+		}
+
+		protected override void AdvanceCounter()
+		{
+			if (++engineState[12] == 0)
+			{
+				++engineState[13];
+			}
+		}
+
+		protected override void ResetCounter()
+		{
+			engineState[12] = engineState[13] = 0;
+		}
+
+		protected override void SetKey(byte[] keyBytes, byte[] ivBytes)
+		{
+			if ((keyBytes.Length != 16) && (keyBytes.Length != 32))
+			{
+				throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key");
+			}
+
+			int offset = 0;
+			byte[] constants;
+
+			// Key
+			engineState[4] = Pack.LE_To_UInt32(keyBytes, 0);
+			engineState[5] = Pack.LE_To_UInt32(keyBytes, 4);
+			engineState[6] = Pack.LE_To_UInt32(keyBytes, 8);
+			engineState[7] = Pack.LE_To_UInt32(keyBytes, 12);
+
+			if (keyBytes.Length == 32)
+			{
+				constants = sigma;
+				offset = 16;
+			} else
+			{
+				constants = tau;
+			}
+
+			engineState[8] = Pack.LE_To_UInt32(keyBytes, offset);
+			engineState[9] = Pack.LE_To_UInt32(keyBytes, offset + 4);
+			engineState[10] = Pack.LE_To_UInt32(keyBytes, offset + 8);
+			engineState[11] = Pack.LE_To_UInt32(keyBytes, offset + 12);
+
+			engineState[0] = Pack.LE_To_UInt32(constants, 0);
+			engineState[1] = Pack.LE_To_UInt32(constants, 4);
+			engineState[2] = Pack.LE_To_UInt32(constants, 8);
+			engineState[3] = Pack.LE_To_UInt32(constants, 12);
+
+			// Counter
+			engineState[12] = engineState[13] = 0;
+
+			// IV
+			engineState[14] = Pack.LE_To_UInt32(ivBytes, 0);
+			engineState[15] = Pack.LE_To_UInt32(ivBytes, 4);
+		}
+
+		protected override void GenerateKeyStream(byte[] output)
+		{
+			ChachaCore(rounds, engineState, x);
+			Pack.UInt32_To_LE(x, output, 0);
+		}
+
+		/**
+	     * ChacCha function
+	     *
+	     * @param   input   input data
+	     *
+	     * @return  keystream
+	     */
+		protected internal static void ChachaCore(int rounds, uint[] input, uint[] x)
+		{
+			if (input.Length != 16) {
+				throw new ArgumentException();
+			}
+			if (x.Length != 16) {
+				throw new ArgumentException();
+			}
+			if (rounds % 2 != 0) {
+				throw new ArgumentException("Number of rounds must be even");
+			}
+
+			uint x00 = input[ 0];
+			uint x01 = input[ 1];
+			uint x02 = input[ 2];
+			uint x03 = input[ 3];
+			uint x04 = input[ 4];
+			uint x05 = input[ 5];
+			uint x06 = input[ 6];
+			uint x07 = input[ 7];
+			uint x08 = input[ 8];
+			uint x09 = input[ 9];
+			uint x10 = input[10];
+			uint x11 = input[11];
+			uint x12 = input[12];
+			uint x13 = input[13];
+			uint x14 = input[14];
+			uint x15 = input[15];
+
+			for (int i = rounds; i > 0; i -= 2)
+			{
+				x00 += x04; x12 = R(x12 ^ x00, 16);
+				x08 += x12; x04 = R(x04 ^ x08, 12);
+				x00 += x04; x12 = R(x12 ^ x00, 8);
+				x08 += x12; x04 = R(x04 ^ x08, 7);
+				x01 += x05; x13 = R(x13 ^ x01, 16);
+				x09 += x13; x05 = R(x05 ^ x09, 12);
+				x01 += x05; x13 = R(x13 ^ x01, 8);
+				x09 += x13; x05 = R(x05 ^ x09, 7);
+				x02 += x06; x14 = R(x14 ^ x02, 16);
+				x10 += x14; x06 = R(x06 ^ x10, 12);
+				x02 += x06; x14 = R(x14 ^ x02, 8);
+				x10 += x14; x06 = R(x06 ^ x10, 7);
+				x03 += x07; x15 = R(x15 ^ x03, 16);
+				x11 += x15; x07 = R(x07 ^ x11, 12);
+				x03 += x07; x15 = R(x15 ^ x03, 8);
+				x11 += x15; x07 = R(x07 ^ x11, 7);
+				x00 += x05; x15 = R(x15 ^ x00, 16);
+				x10 += x15; x05 = R(x05 ^ x10, 12);
+				x00 += x05; x15 = R(x15 ^ x00, 8);
+				x10 += x15; x05 = R(x05 ^ x10, 7);
+				x01 += x06; x12 = R(x12 ^ x01, 16);
+				x11 += x12; x06 = R(x06 ^ x11, 12);
+				x01 += x06; x12 = R(x12 ^ x01, 8);
+				x11 += x12; x06 = R(x06 ^ x11, 7);
+				x02 += x07; x13 = R(x13 ^ x02, 16);
+				x08 += x13; x07 = R(x07 ^ x08, 12);
+				x02 += x07; x13 = R(x13 ^ x02, 8);
+				x08 += x13; x07 = R(x07 ^ x08, 7);
+				x03 += x04; x14 = R(x14 ^ x03, 16);
+				x09 += x14; x04 = R(x04 ^ x09, 12);
+				x03 += x04; x14 = R(x14 ^ x03, 8);
+				x09 += x14; x04 = R(x04 ^ x09, 7);
+
+			}
+
+			x[ 0] = x00 + input[ 0];
+			x[ 1] = x01 + input[ 1];
+			x[ 2] = x02 + input[ 2];
+			x[ 3] = x03 + input[ 3];
+			x[ 4] = x04 + input[ 4];
+			x[ 5] = x05 + input[ 5];
+			x[ 6] = x06 + input[ 6];
+			x[ 7] = x07 + input[ 7];
+			x[ 8] = x08 + input[ 8];
+			x[ 9] = x09 + input[ 9];
+			x[10] = x10 + input[10];
+			x[11] = x11 + input[11];
+			x[12] = x12 + input[12];
+			x[13] = x13 + input[13];
+			x[14] = x14 + input[14];
+			x[15] = x15 + input[15];
+		}
+
+	}
+
+}
+
diff --git a/crypto/src/crypto/engines/Salsa20Engine.cs b/crypto/src/crypto/engines/Salsa20Engine.cs
index 7d68deab1..5698737de 100644
--- a/crypto/src/crypto/engines/Salsa20Engine.cs
+++ b/crypto/src/crypto/engines/Salsa20Engine.cs
@@ -10,27 +10,30 @@ namespace Org.BouncyCastle.Crypto.Engines
 	/**
 	 * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
 	 */
+	[CLSCompliantAttribute(false)]
 	public class Salsa20Engine
 		: IStreamCipher
 	{
+		public static readonly int DEFAULT_ROUNDS = 20;
+
 		/** Constants */
 		private const int StateSize = 16; // 16, 32 bit ints = 64 bytes
 
-		private readonly static byte[]
+		protected readonly static byte[]
 			sigma = Strings.ToAsciiByteArray("expand 32-byte k"),
 			tau = Strings.ToAsciiByteArray("expand 16-byte k");
 
+		protected int rounds;
+
 		/*
 		 * variables to hold the state of the engine
 		 * during encryption and decryption
 		 */
-		private int		index = 0;
-		private uint[]  engineState = new uint[StateSize]; // state
-		private uint[]  x = new uint[StateSize]; // internal buffer
-		private byte[]  keyStream = new byte[StateSize * 4], // expanded state, 64 bytes
-						workingKey  = null,
-						workingIV   = null;
-		private bool	initialised = false;
+		private int		 index = 0;
+		protected uint[] engineState = new uint[StateSize]; // state
+		protected uint[] x = new uint[StateSize]; // internal buffer
+		private byte[]	 keyStream = new byte[StateSize * 4]; // expanded state, 64 bytes
+		private bool	 initialised = false;
 
 		/*
 		 * internal counter
@@ -38,6 +41,28 @@ namespace Org.BouncyCastle.Crypto.Engines
 		private uint cW0, cW1, cW2;
 
 		/**
+		 * Creates a 20 round Salsa20 engine.
+		 */
+		public Salsa20Engine()
+			: this(DEFAULT_ROUNDS)
+		{
+		}
+
+		/**
+		 * Creates a Salsa20 engine with a specific number of rounds.
+		 * @param rounds the number of rounds (must be an even number).
+		 */
+		public Salsa20Engine(int rounds)
+		{
+			if (rounds <= 0 || (rounds & 1) != 0)
+			{
+				throw new ArgumentException("'rounds' must be a positive, even number");
+			}
+
+			this.rounds = rounds;
+		}
+
+		/**
 		 * initialise a Salsa20 cipher.
 		 *
 		 * @param forEncryption whether or not we are for encryption.
@@ -58,27 +83,38 @@ namespace Org.BouncyCastle.Crypto.Engines
 			ParametersWithIV ivParams = parameters as ParametersWithIV;
 
 			if (ivParams == null)
-				throw new ArgumentException("Salsa20 Init requires an IV", "parameters");
+				throw new ArgumentException(AlgorithmName + " Init requires an IV", "parameters");
 
 			byte[] iv = ivParams.GetIV();
 
-			if (iv == null || iv.Length != 8)
-				throw new ArgumentException("Salsa20 requires exactly 8 bytes of IV");
+			if (iv == null || iv.Length != NonceSize)
+				throw new ArgumentException(AlgorithmName + " requires exactly " + NonceSize + " bytes of IV");
 
 			KeyParameter key = ivParams.Parameters as KeyParameter;
 
 			if (key == null)
-				throw new ArgumentException("Salsa20 Init requires a key", "parameters");
+				throw new ArgumentException(AlgorithmName + " Init requires a key", "parameters");
 
-			workingKey = key.GetKey();
-			workingIV = iv;
+			SetKey(key.GetKey(), iv);
+			Reset();
+			initialised = true;
+		}
 
-			SetKey(workingKey, workingIV);
+		protected virtual int NonceSize
+		{
+			get { return 8; }
 		}
 
-		public string AlgorithmName
+		public virtual string AlgorithmName
 		{
-			get { return "Salsa20"; }
+			get { 
+				string name = "Salsa20";
+				if (rounds != DEFAULT_ROUNDS)
+				{
+					name += "/" + rounds;
+				}
+				return name;
+			}
 		}
 
 		public byte ReturnByte(
@@ -92,11 +128,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			if (index == 0)
 			{
 				GenerateKeyStream(keyStream);
-
-				if (++engineState[8] == 0)
-				{
-					++engineState[9];
-				}
+				AdvanceCounter();
 			}
 
 			byte output = (byte)(keyStream[index] ^ input);
@@ -105,6 +137,14 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return output;
 		}
 
+		protected virtual void AdvanceCounter()
+		{
+			if (++engineState[8] == 0)
+			{
+				++engineState[9];
+			}
+		}
+
 		public void ProcessBytes(
 			byte[]	inBytes, 
 			int		inOff, 
@@ -137,11 +177,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 				if (index == 0)
 				{
 					GenerateKeyStream(keyStream);
-
-					if (++engineState[8] == 0)
-					{
-						++engineState[9];
-					}
+					AdvanceCounter();
 				}
 				outBytes[i+outOff] = (byte)(keyStream[index]^inBytes[i+inOff]);
 				index = (index + 1) & 63;
@@ -150,28 +186,32 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 		public void Reset()
 		{
-			SetKey(workingKey, workingIV);
+			index = 0;
+			ResetLimitCounter();
+			ResetCounter();
 		}
 
-		// Private implementation
+		protected virtual void ResetCounter()
+		{
+			engineState[8] = engineState[9] = 0;
+		}
 
-		private void SetKey(byte[] keyBytes, byte[] ivBytes)
+		protected virtual void SetKey(byte[] keyBytes, byte[] ivBytes)
 		{
-			workingKey = keyBytes;
-			workingIV  = ivBytes;
+			if ((keyBytes.Length != 16) && (keyBytes.Length != 32)) {
+				throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key");
+			}
 
-			index = 0;
-			ResetCounter();
 			int offset = 0;
 			byte[] constants;
 
 			// Key
-			engineState[1] = Pack.LE_To_UInt32(workingKey, 0);
-			engineState[2] = Pack.LE_To_UInt32(workingKey, 4);
-			engineState[3] = Pack.LE_To_UInt32(workingKey, 8);
-			engineState[4] = Pack.LE_To_UInt32(workingKey, 12);
+			engineState[1] = Pack.LE_To_UInt32(keyBytes, 0);
+			engineState[2] = Pack.LE_To_UInt32(keyBytes, 4);
+			engineState[3] = Pack.LE_To_UInt32(keyBytes, 8);
+			engineState[4] = Pack.LE_To_UInt32(keyBytes, 12);
 
-			if (workingKey.Length == 32)
+			if (keyBytes.Length == 32)
 			{
 				constants = sigma;
 				offset = 16;
@@ -181,83 +221,125 @@ namespace Org.BouncyCastle.Crypto.Engines
 				constants = tau;
 			}
 
-			engineState[11] = Pack.LE_To_UInt32(workingKey, offset);
-			engineState[12] = Pack.LE_To_UInt32(workingKey, offset + 4);
-			engineState[13] = Pack.LE_To_UInt32(workingKey, offset + 8);
-			engineState[14] = Pack.LE_To_UInt32(workingKey, offset + 12);
+			engineState[11] = Pack.LE_To_UInt32(keyBytes, offset);
+			engineState[12] = Pack.LE_To_UInt32(keyBytes, offset + 4);
+			engineState[13] = Pack.LE_To_UInt32(keyBytes, offset + 8);
+			engineState[14] = Pack.LE_To_UInt32(keyBytes, offset + 12);
 			engineState[0] = Pack.LE_To_UInt32(constants, 0);
 			engineState[5] = Pack.LE_To_UInt32(constants, 4);
 			engineState[10] = Pack.LE_To_UInt32(constants, 8);
 			engineState[15] = Pack.LE_To_UInt32(constants, 12);
 
 			// IV
-			engineState[6] = Pack.LE_To_UInt32(workingIV, 0);
-			engineState[7] = Pack.LE_To_UInt32(workingIV, 4);
-			engineState[8] = engineState[9] = 0;
-
-			initialised = true;
+			engineState[6] = Pack.LE_To_UInt32(ivBytes, 0);
+			engineState[7] = Pack.LE_To_UInt32(ivBytes, 4);
+			ResetCounter();
 		}
 
-		private void GenerateKeyStream(byte[] output)
+		protected virtual void GenerateKeyStream(byte[] output)
 		{
-			SalsaCore(20, engineState, x);
+			SalsaCore(rounds, engineState, x);
 			Pack.UInt32_To_LE(x, output, 0);
 		}
 
-		internal static void SalsaCore(int rounds, uint[] state, uint[] x)
+		protected internal static void SalsaCore(int rounds, uint[] input, uint[] x)
 		{
-            // TODO Exception if rounds odd?
+			if (input.Length != 16) {
+				throw new ArgumentException();
+			}
+			if (x.Length != 16) {
+				throw new ArgumentException();
+			}
+			if (rounds % 2 != 0) {
+				throw new ArgumentException("Number of rounds must be even");
+			}
 
-            Array.Copy(state, 0, x, 0, state.Length);
+			uint x00 = input[ 0];
+			uint x01 = input[ 1];
+			uint x02 = input[ 2];
+			uint x03 = input[ 3];
+			uint x04 = input[ 4];
+			uint x05 = input[ 5];
+			uint x06 = input[ 6];
+			uint x07 = input[ 7];
+			uint x08 = input[ 8];
+			uint x09 = input[ 9];
+			uint x10 = input[10];
+			uint x11 = input[11];
+			uint x12 = input[12];
+			uint x13 = input[13];
+			uint x14 = input[14];
+			uint x15 = input[15];
 
 			for (int i = rounds; i > 0; i -= 2)
 			{
-				x[ 4] ^= R((x[ 0]+x[12]), 7);
-				x[ 8] ^= R((x[ 4]+x[ 0]), 9);
-				x[12] ^= R((x[ 8]+x[ 4]),13);
-				x[ 0] ^= R((x[12]+x[ 8]),18);
-				x[ 9] ^= R((x[ 5]+x[ 1]), 7);
-				x[13] ^= R((x[ 9]+x[ 5]), 9);
-				x[ 1] ^= R((x[13]+x[ 9]),13);
-				x[ 5] ^= R((x[ 1]+x[13]),18);
-				x[14] ^= R((x[10]+x[ 6]), 7);
-				x[ 2] ^= R((x[14]+x[10]), 9);
-				x[ 6] ^= R((x[ 2]+x[14]),13);
-				x[10] ^= R((x[ 6]+x[ 2]),18);
-				x[ 3] ^= R((x[15]+x[11]), 7);
-				x[ 7] ^= R((x[ 3]+x[15]), 9);
-				x[11] ^= R((x[ 7]+x[ 3]),13);
-				x[15] ^= R((x[11]+x[ 7]),18);
-				x[ 1] ^= R((x[ 0]+x[ 3]), 7);
-				x[ 2] ^= R((x[ 1]+x[ 0]), 9);
-				x[ 3] ^= R((x[ 2]+x[ 1]),13);
-				x[ 0] ^= R((x[ 3]+x[ 2]),18);
-				x[ 6] ^= R((x[ 5]+x[ 4]), 7);
-				x[ 7] ^= R((x[ 6]+x[ 5]), 9);
-				x[ 4] ^= R((x[ 7]+x[ 6]),13);
-				x[ 5] ^= R((x[ 4]+x[ 7]),18);
-				x[11] ^= R((x[10]+x[ 9]), 7);
-				x[ 8] ^= R((x[11]+x[10]), 9);
-				x[ 9] ^= R((x[ 8]+x[11]),13);
-				x[10] ^= R((x[ 9]+x[ 8]),18);
-				x[12] ^= R((x[15]+x[14]), 7);
-				x[13] ^= R((x[12]+x[15]), 9);
-				x[14] ^= R((x[13]+x[12]),13);
-				x[15] ^= R((x[14]+x[13]),18);
+				x04 ^= R((x00+x12), 7);
+				x08 ^= R((x04+x00), 9);
+				x12 ^= R((x08+x04),13);
+				x00 ^= R((x12+x08),18);
+				x09 ^= R((x05+x01), 7);
+				x13 ^= R((x09+x05), 9);
+				x01 ^= R((x13+x09),13);
+				x05 ^= R((x01+x13),18);
+				x14 ^= R((x10+x06), 7);
+				x02 ^= R((x14+x10), 9);
+				x06 ^= R((x02+x14),13);
+				x10 ^= R((x06+x02),18);
+				x03 ^= R((x15+x11), 7);
+				x07 ^= R((x03+x15), 9);
+				x11 ^= R((x07+x03),13);
+				x15 ^= R((x11+x07),18);
+
+				x01 ^= R((x00+x03), 7);
+				x02 ^= R((x01+x00), 9);
+				x03 ^= R((x02+x01),13);
+				x00 ^= R((x03+x02),18);
+				x06 ^= R((x05+x04), 7);
+				x07 ^= R((x06+x05), 9);
+				x04 ^= R((x07+x06),13);
+				x05 ^= R((x04+x07),18);
+				x11 ^= R((x10+x09), 7);
+				x08 ^= R((x11+x10), 9);
+				x09 ^= R((x08+x11),13);
+				x10 ^= R((x09+x08),18);
+				x12 ^= R((x15+x14), 7);
+				x13 ^= R((x12+x15), 9);
+				x14 ^= R((x13+x12),13);
+				x15 ^= R((x14+x13),18);
 			}
 
-			for (int i = 0; i < StateSize; ++i)
-			{
-				x[i] += state[i];
-			}
+			x[ 0] = x00 + input[ 0];
+			x[ 1] = x01 + input[ 1];
+			x[ 2] = x02 + input[ 2];
+			x[ 3] = x03 + input[ 3];
+			x[ 4] = x04 + input[ 4];
+			x[ 5] = x05 + input[ 5];
+			x[ 6] = x06 + input[ 6];
+			x[ 7] = x07 + input[ 7];
+			x[ 8] = x08 + input[ 8];
+			x[ 9] = x09 + input[ 9];
+			x[10] = x10 + input[10];
+			x[11] = x11 + input[11];
+			x[12] = x12 + input[12];
+			x[13] = x13 + input[13];
+			x[14] = x14 + input[14];
+			x[15] = x15 + input[15];
 		}
 
-		private static uint R(uint x, int y)
+		/**
+		 * Rotate left
+		 *
+		 * @param   x   value to rotate
+		 * @param   y   amount to rotate x
+		 *
+		 * @return  rotated x
+		 */
+		protected static uint R(uint x, int y)
 		{
 			return (x << y) | (x >> (32 - y));
 		}
 
-		private void ResetCounter()
+		private void ResetLimitCounter()
 		{
 			cW0 = 0;
 			cW1 = 0;
diff --git a/crypto/src/crypto/engines/XSalsa20Engine.cs b/crypto/src/crypto/engines/XSalsa20Engine.cs
new file mode 100644
index 000000000..6d61c32cd
--- /dev/null
+++ b/crypto/src/crypto/engines/XSalsa20Engine.cs
@@ -0,0 +1,70 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+	/**
+	 * Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce.
+	 * <p>
+	 * XSalsa20 requires a 256 bit key, and a 192 bit nonce.
+	 */
+	public class XSalsa20Engine 
+		: Salsa20Engine
+	{
+
+		public override string AlgorithmName
+		{
+			get { return "XSalsa20"; }
+		}
+
+		protected override int NonceSize
+		{
+			get { return 24; }
+		}
+
+		/**
+		 * XSalsa20 key generation: process 256 bit input key and 128 bits of the input nonce
+		 * using a core Salsa20 function without input addition to produce 256 bit working key
+		 * and use that with the remaining 64 bits of nonce to initialize a standard Salsa20 engine state.
+		 */
+		protected override void SetKey(byte[] keyBytes, byte[] ivBytes)
+		{
+			if (keyBytes.Length != 32)
+			{
+				throw new ArgumentException(AlgorithmName + " requires a 256 bit key");
+			}
+
+			// Set key for HSalsa20
+			base.SetKey(keyBytes, ivBytes);
+
+			// Pack next 64 bits of IV into engine state instead of counter
+			engineState[8] = Pack.LE_To_UInt32(ivBytes, 8);
+			engineState[9] = Pack.LE_To_UInt32(ivBytes, 12);
+
+			// Process engine state to generate Salsa20 key
+			uint[] hsalsa20Out = new uint[engineState.Length];
+			SalsaCore(20, engineState, hsalsa20Out);
+
+			// Set new key, removing addition in last round of salsaCore
+			engineState[1] = hsalsa20Out[0] - engineState[0];
+			engineState[2] = hsalsa20Out[5] - engineState[5];
+			engineState[3] = hsalsa20Out[10] - engineState[10];
+			engineState[4] = hsalsa20Out[15] - engineState[15];
+
+			engineState[11] = hsalsa20Out[6] - engineState[6];
+			engineState[12] = hsalsa20Out[7] - engineState[7];
+			engineState[13] = hsalsa20Out[8] - engineState[8];
+			engineState[14] = hsalsa20Out[9] - engineState[9];
+
+			// Last 64 bits of input IV
+			engineState[6] = Pack.LE_To_UInt32(ivBytes, 16);
+			engineState[7] = Pack.LE_To_UInt32(ivBytes, 20);
+
+			// Counter reset
+			ResetCounter();
+		}
+
+	}
+}
+
diff --git a/crypto/test/src/crypto/test/ChaChaTest.cs b/crypto/test/src/crypto/test/ChaChaTest.cs
new file mode 100644
index 000000000..fea88ca85
--- /dev/null
+++ b/crypto/test/src/crypto/test/ChaChaTest.cs
@@ -0,0 +1,318 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+	/**
+	 * ChaCha Test
+	 * <p>
+	 * Test cases generated using ref version of ChaCha20 in estreambench-20080905.
+	 */
+	[TestFixture]
+	public class ChaChaTest
+		: SimpleTest
+	{
+		private static readonly byte[] zeroes = Hex.Decode(
+			"00000000000000000000000000000000"
+			+ "00000000000000000000000000000000"
+			+ "00000000000000000000000000000000"
+			+ "00000000000000000000000000000000");
+
+		private static readonly string set1v0_0 =
+			"FBB87FBB8395E05DAA3B1D683C422046"
+			+ "F913985C2AD9B23CFC06C1D8D04FF213"
+			+ "D44A7A7CDB84929F915420A8A3DC58BF"
+			+ "0F7ECB4B1F167BB1A5E6153FDAF4493D";
+
+		private static readonly string set1v0_192 =
+			"D9485D55B8B82D792ED1EEA8E93E9BC1"
+			+ "E2834AD0D9B11F3477F6E106A2F6A5F2"
+			+ "EA8244D5B925B8050EAB038F58D4DF57"
+			+ "7FAFD1B89359DAE508B2B10CBD6B488E";
+
+		private static readonly string set1v0_256 =
+			"08661A35D6F02D3D9ACA8087F421F7C8"
+			+ "A42579047D6955D937925BA21396DDD4"
+			+ "74B1FC4ACCDCAA33025B4BCE817A4FBF"
+			+ "3E5D07D151D7E6FE04934ED466BA4779";
+
+		private static readonly string set1v0_448 =
+			"A7E16DD38BA48CCB130E5BE9740CE359"
+			+ "D631E91600F85C8A5D0785A612D1D987"
+			+ "90780ACDDC26B69AB106CCF6D866411D"
+			+ "10637483DBF08CC5591FD8B3C87A3AE0";
+
+		private static readonly string set1v9_0 =
+			"A276339F99316A913885A0A4BE870F06"
+			+ "91E72B00F1B3F2239F714FE81E88E00C"
+			+ "BBE52B4EBBE1EA15894E29658C4CB145"
+			+ "E6F89EE4ABB045A78514482CE75AFB7C";
+
+		private static readonly string set1v9_192 =
+			"0DFB9BD4F87F68DE54FBC1C6428FDEB0"
+			+ "63E997BE8490C9B7A4694025D6EBA2B1"
+			+ "5FE429DB82A7CAE6AAB22918E8D00449"
+			+ "6FB6291467B5AE81D4E85E81D8795EBB";
+
+		private static readonly string set1v9_256 =
+			"546F5BB315E7F71A46E56D4580F90889"
+			+ "639A2BA528F757CF3B048738BA141AF3"
+			+ "B31607CB21561BAD94721048930364F4"
+			+ "B1227CFEB7CDECBA881FB44903550E68";
+
+		private static readonly string set1v9_448 =
+			"6F813586E76691305A0CF048C0D8586D"
+			+ "C89460207D8B230CD172398AA33D19E9"
+			+ "2D24883C3A9B0BB7CD8C6B2668DB142E"
+			+ "37A97948A7A01498A21110297984CD20";
+
+		private static readonly string set6v0_0 =
+			"57459975BC46799394788DE80B928387"
+			+ "862985A269B9E8E77801DE9D874B3F51"
+			+ "AC4610B9F9BEE8CF8CACD8B5AD0BF17D"
+			+ "3DDF23FD7424887EB3F81405BD498CC3";
+
+		private static readonly string set6v0_65472 =
+			"EF9AEC58ACE7DB427DF012B2B91A0C1E"
+			+ "8E4759DCE9CDB00A2BD59207357BA06C"
+			+ "E02D327C7719E83D6348A6104B081DB0"
+			+ "3908E5186986AE41E3AE95298BB7B713";
+
+		private static readonly string set6v0_65536 =
+			"17EF5FF454D85ABBBA280F3A94F1D26E"
+			+ "950C7D5B05C4BB3A78326E0DC5731F83"
+			+ "84205C32DB867D1B476CE121A0D7074B"
+			+ "AA7EE90525D15300F48EC0A6624BD0AF";
+
+		private static readonly string set6v1_0 =
+			"92A2508E2C4084567195F2A1005E552B"
+			+ "4874EC0504A9CD5E4DAF739AB553D2E7"
+			+ "83D79C5BA11E0653BEBB5C116651302E"
+			+ "8D381CB728CA627B0B246E83942A2B99";
+
+		private static readonly string set6v1_65472 =
+			"E1974EC3063F7BD0CBA58B1CE34BC874"
+			+ "67AAF5759B05EA46682A5D4306E5A76B"
+			+ "D99A448DB8DE73AF97A73F5FBAE2C776"
+			+ "35040464524CF14D7F08D4CE1220FD84";
+
+		private static readonly string set6v1_65536 =
+			"BE3436141CFD62D12FF7D852F80C1344"
+			+ "81F152AD0235ECF8CA172C55CA8C031B"
+			+ "2E785D773A988CA8D4BDA6FAE0E493AA"
+			+ "71DCCC4C894D1F106CAC62A9FC0A9607";
+
+		// ChaCha12
+		private static readonly string chacha12_set1v0_0 =
+			"36CF0D56E9F7FBF287BC5460D95FBA94"
+			+ "AA6CBF17D74E7C784DDCF7E0E882DDAE"
+			+ "3B5A58243EF32B79A04575A8E2C2B73D"
+			+ "C64A52AA15B9F88305A8F0CA0B5A1A25";
+
+		private static readonly string chacha12_set1v0_192 =
+			"83496792AB68FEC75ADB16D3044420A4"
+			+ "A00A6E9ADC41C3A63DBBF317A8258C85"
+			+ "A9BC08B4F76B413A4837324AEDF8BC2A"
+			+ "67D53C9AB9E1C5BC5F379D48DF9AF730";
+
+		private static readonly string chacha12_set1v0_256 = 
+			"BAA28ED593690FD760ADA07C95E3B888"
+			+ "4B4B64E488CA7A2D9BDC262243AB9251"
+			+ "394C5037E255F8BCCDCD31306C508FFB"
+			+ "C9E0161380F7911FCB137D46D9269250";
+
+		private static readonly string chacha12_set1v0_448 =
+			"B7ECFB6AE0B51915762FE1FD03A14D0C"
+			+ "9E54DA5DC76EB16EBA5313BC535DE63D"
+			+ "C72D7F9F1874E301E99C8531819F4E37"
+			+ "75793F6A5D19C717FA5C78A39EB804A6";
+
+		// ChaCha8
+		private static readonly string chacha8_set1v0_0 =
+			"BEB1E81E0F747E43EE51922B3E87FB38"
+			+ "D0163907B4ED49336032AB78B67C2457"
+			+ "9FE28F751BD3703E51D876C017FAA435"
+			+ "89E63593E03355A7D57B2366F30047C5";
+
+		private static readonly string chacha8_set1v0_192 =
+			"33B8B7CA8F8E89F0095ACE75A379C651"
+			+ "FD6BDD55703C90672E44C6BAB6AACDD8"
+			+ "7C976A87FD264B906E749429284134C2"
+			+ "38E3B88CF74A68245B860D119A8BDF43";
+
+		private static readonly string chacha8_set1v0_256 =
+			"F7CA95BF08688BD3BE8A27724210F9DC"
+			+ "16F32AF974FBFB09E9F757C577A245AB"
+			+ "F35F824B70A4C02CB4A8D7191FA8A5AD"
+			+ "6A84568743844703D353B7F00A8601F4";
+
+		private static readonly string chacha8_set1v0_448 =
+			"7B4117E8BFFD595CD8482270B08920FB"
+			+ "C9B97794E1809E07BB271BF07C861003"
+			+ "4C38DBA6ECA04E5474F399A284CBF6E2"
+			+ "7F70142E604D0977797DE5B58B6B25E0";
+
+		public override string Name
+		{
+			get { return "ChaCha"; }
+		}
+
+		public override void PerformTest()
+		{
+			chachaTest1(20, new ParametersWithIV(new KeyParameter(Hex.Decode("80000000000000000000000000000000")), Hex.Decode("0000000000000000")),
+					set1v0_0, set1v0_192,  set1v0_256,  set1v0_448);
+			chachaTest1(20, new ParametersWithIV(new KeyParameter(Hex.Decode("00400000000000000000000000000000")), Hex.Decode("0000000000000000")),
+					set1v9_0, set1v9_192,  set1v9_256,  set1v9_448);
+			chachaTest1(12, new ParametersWithIV(new KeyParameter(Hex.Decode("80000000000000000000000000000000")), Hex.Decode("0000000000000000")),
+		            chacha12_set1v0_0, chacha12_set1v0_192,  chacha12_set1v0_256,  chacha12_set1v0_448);
+			chachaTest1(8, new ParametersWithIV(new KeyParameter(Hex.Decode("80000000000000000000000000000000")), Hex.Decode("0000000000000000")),
+		            chacha8_set1v0_0, chacha8_set1v0_192,  chacha8_set1v0_256,  chacha8_set1v0_448);
+			chachaTest2(new ParametersWithIV(new KeyParameter(Hex.Decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.Decode("0D74DB42A91077DE")),
+					set6v0_0, set6v0_65472, set6v0_65536);
+			chachaTest2(new ParametersWithIV(new KeyParameter(Hex.Decode("0558ABFE51A4F74A9DF04396E93C8FE23588DB2E81D4277ACD2073C6196CBF12")), Hex.Decode("167DE44BB21980E7")),
+					set6v1_0, set6v1_65472, set6v1_65536);
+			reinitBug();
+		}
+
+		private void chachaTest1(
+			int rounds,
+			ICipherParameters	parameters,
+			string				v0,
+			string				v192,
+			string				v256,
+			string				v448)
+		{
+			IStreamCipher salsa = new ChaChaEngine(rounds);
+			byte[]       buf = new byte[64];
+
+			salsa.Init(true, parameters);
+
+			for (int i = 0; i != 7; i++)
+			{
+				salsa.ProcessBytes(zeroes, 0, 64, buf, 0);
+				switch (i)
+				{
+				case 0:
+					if (!AreEqual(buf, Hex.Decode(v0)))
+					{
+						mismatch("v0/" + rounds, v0, buf);
+					}
+					break;
+				case 3:
+					if (!AreEqual(buf, Hex.Decode(v192)))
+					{
+						mismatch("v192/" + rounds, v192, buf);
+					}
+					break;
+				case 4:
+					if (!AreEqual(buf, Hex.Decode(v256)))
+					{
+						mismatch("v256/" + rounds, v256, buf);
+					}
+					break;
+				default:
+					// ignore
+					break;
+				}
+			}
+
+			for (int i = 0; i != 64; i++)
+			{
+				buf[i] = salsa.ReturnByte(zeroes[i]);
+			}
+
+			if (!AreEqual(buf, Hex.Decode(v448)))
+			{
+				mismatch("v448", v448, buf);
+			}       
+		}
+
+		private void chachaTest2(
+			ICipherParameters	parameters,
+			string				v0,
+			string				v65472,
+			string				v65536)
+		{
+			IStreamCipher salsa = new ChaChaEngine();
+			byte[]       buf = new byte[64];
+
+			salsa.Init(true, parameters);
+
+			for (int i = 0; i != 1025; i++)
+			{
+				salsa.ProcessBytes(zeroes, 0, 64, buf, 0);
+				switch (i)
+				{
+				case 0:
+					if (!AreEqual(buf, Hex.Decode(v0)))
+					{
+						mismatch("v0", v0, buf);
+					}
+					break;
+				case 1023:
+					if (!AreEqual(buf, Hex.Decode(v65472)))
+					{
+						mismatch("v65472", v65472, buf);
+					}
+					break;
+				case 1024:
+					if (!AreEqual(buf, Hex.Decode(v65536)))
+					{
+						mismatch("v65536", v65536, buf);
+					}
+					break;
+				default:
+					// ignore
+					break;
+				}
+			}
+		}
+
+		private void mismatch(
+			string	name,
+			string	expected,
+			byte[]	found)
+		{
+			Fail("mismatch on " + name, expected, Hex.ToHexString(found));
+		}
+
+		private void reinitBug()
+		{
+			KeyParameter key = new KeyParameter(Hex.Decode("80000000000000000000000000000000"));
+			ParametersWithIV parameters = new ParametersWithIV(key, Hex.Decode("0000000000000000"));
+
+			IStreamCipher chacha = new ChaChaEngine();
+
+			chacha.Init(true, parameters);
+
+			try
+			{
+				chacha.Init(true, key);
+				Fail("ChaCha should throw exception if no IV in Init");
+			}
+			catch (ArgumentException)
+			{
+			}
+		}
+
+		public static void Main(
+			string[] args)
+		{
+			RunTest(new ChaChaTest());
+		}
+
+		[Test]
+		public void TestFunction()
+		{
+			string resultText = Perform().ToString();
+
+			Assert.AreEqual(Name + ": Okay", resultText);
+		}
+	}
+}
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index 687e9ee4f..3a21f3656 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -94,7 +94,9 @@ namespace Org.BouncyCastle.Crypto.Tests
             new Rfc3211WrapTest(),
             new SeedTest(),
             new NaccacheSternTest(),
-            new Salsa20Test(),
+			new Salsa20Test(),
+			new XSalsa20Test(),
+			new ChaChaTest(),
             new CMacTest(),
             new EaxTest(),
             new GcmTest(),
diff --git a/crypto/test/src/crypto/test/Salsa20Test.cs b/crypto/test/src/crypto/test/Salsa20Test.cs
index 9e7549ae7..b4dc1ef2b 100644
--- a/crypto/test/src/crypto/test/Salsa20Test.cs
+++ b/crypto/test/src/crypto/test/Salsa20Test.cs
@@ -106,6 +106,58 @@ namespace Org.BouncyCastle.Crypto.Tests
 			+ "C945A6CC69A6A17367BC03431A86B3ED"
 			+ "04B0245B56379BF997E25800AD837D7D";
 
+		// Salsa20/12
+
+		private static readonly string salsa12_set1v0_0 = 
+			"FC207DBFC76C5E1774961E7A5AAD0906"
+			+ "9B2225AC1CE0FE7A0CE77003E7E5BDF8"
+			+ "B31AF821000813E6C56B8C1771D6EE70"
+			+ "39B2FBD0A68E8AD70A3944B677937897";
+
+		private static readonly string salsa12_set1v0_192 = 
+			"4B62A4881FA1AF9560586510D5527ED4"
+			+ "8A51ECAFA4DECEEBBDDC10E9918D44AB"
+			+ "26B10C0A31ED242F146C72940C6E9C37"
+			+ "53F641DA84E9F68B4F9E76B6C48CA5AC";
+
+		private static readonly string salsa12_set1v0_256 = 
+			"F52383D9DEFB20810325F7AEC9EADE34"
+			+ "D9D883FEE37E05F74BF40875B2D0BE79"
+			+ "ED8886E5BFF556CEA8D1D9E86B1F68A9"
+			+ "64598C34F177F8163E271B8D2FEB5996";
+
+		private static readonly string salsa12_set1v0_448 =
+			"A52ED8C37014B10EC0AA8E05B5CEEE12"
+			+ "3A1017557FB3B15C53E6C5EA8300BF74"
+			+ "264A73B5315DC821AD2CAB0F3BB2F152"
+			+ "BDAEA3AEE97BA04B8E72A7B40DCC6BA4";
+
+		// Salsa20/8
+
+		private static readonly string salsa8_set1v0_0 =
+			"A9C9F888AB552A2D1BBFF9F36BEBEB33"
+			+ "7A8B4B107C75B63BAE26CB9A235BBA9D"
+			+ "784F38BEFC3ADF4CD3E266687EA7B9F0"
+			+ "9BA650AE81EAC6063AE31FF12218DDC5";
+
+		private static readonly string salsa8_set1v0_192 =
+			"BB5B6BB2CC8B8A0222DCCC1753ED4AEB"
+			+ "23377ACCBD5D4C0B69A8A03BB115EF71"
+			+ "871BC10559080ACA7C68F0DEF32A80DD"
+			+ "BAF497259BB76A3853A7183B51CC4B9F";
+
+		private static readonly string salsa8_set1v0_256 =
+			"4436CDC0BE39559F5E5A6B79FBDB2CAE"
+			+ "4782910F27FFC2391E05CFC78D601AD8"
+			+ "CD7D87B074169361D997D1BED9729C0D"
+			+ "EB23418E0646B7997C06AA84E7640CE3";
+
+		private static readonly string salsa8_set1v0_448 =
+			"BEE85903BEA506B05FC04795836FAAAC"
+			+ "7F93F785D473EB762576D96B4A65FFE4"
+			+ "63B34AAE696777FC6351B67C3753B89B"
+			+ "A6B197BD655D1D9CA86E067F4D770220";
+
 		public override string Name
 		{
 			get { return "Salsa20"; }
@@ -113,10 +165,14 @@ namespace Org.BouncyCastle.Crypto.Tests
 
 		public override void PerformTest()
 		{
-			salsa20Test1(new ParametersWithIV(new KeyParameter(Hex.Decode("80000000000000000000000000000000")), Hex.Decode("0000000000000000")),
+			salsa20Test1(20, new ParametersWithIV(new KeyParameter(Hex.Decode("80000000000000000000000000000000")), Hex.Decode("0000000000000000")),
 					set1v0_0, set1v0_192,  set1v0_256,  set1v0_448);
-			salsa20Test1(new ParametersWithIV(new KeyParameter(Hex.Decode("00400000000000000000000000000000")), Hex.Decode("0000000000000000")),
+			salsa20Test1(20, new ParametersWithIV(new KeyParameter(Hex.Decode("00400000000000000000000000000000")), Hex.Decode("0000000000000000")),
 					set1v9_0, set1v9_192,  set1v9_256,  set1v9_448);
+			salsa20Test1(12, new ParametersWithIV(new KeyParameter(Hex.Decode("80000000000000000000000000000000")), Hex.Decode("0000000000000000")),
+		             salsa12_set1v0_0, salsa12_set1v0_192,  salsa12_set1v0_256,  salsa12_set1v0_448);
+			salsa20Test1(8, new ParametersWithIV(new KeyParameter(Hex.Decode("80000000000000000000000000000000")), Hex.Decode("0000000000000000")),
+		             salsa8_set1v0_0, salsa8_set1v0_192,  salsa8_set1v0_256,  salsa8_set1v0_448);
 			salsa20Test2(new ParametersWithIV(new KeyParameter(Hex.Decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.Decode("0D74DB42A91077DE")),
 					set6v0_0, set6v0_65472, set6v0_65536);
 			salsa20Test2(new ParametersWithIV(new KeyParameter(Hex.Decode("0558ABFE51A4F74A9DF04396E93C8FE23588DB2E81D4277ACD2073C6196CBF12")), Hex.Decode("167DE44BB21980E7")),
@@ -125,13 +181,14 @@ namespace Org.BouncyCastle.Crypto.Tests
 		}
 
 		private void salsa20Test1(
+			int rounds,
 			ICipherParameters	parameters,
 			string				v0,
 			string				v192,
 			string				v256,
 			string				v448)
 		{
-			IStreamCipher salsa = new Salsa20Engine();
+			IStreamCipher salsa = new Salsa20Engine(rounds);
 			byte[]       buf = new byte[64];
 
 			salsa.Init(true, parameters);
@@ -144,19 +201,19 @@ namespace Org.BouncyCastle.Crypto.Tests
 				case 0:
 					if (!AreEqual(buf, Hex.Decode(v0)))
 					{
-						mismatch("v0", v0, buf);
+						mismatch("v0/" + rounds, v0, buf);
 					}
 					break;
 				case 3:
 					if (!AreEqual(buf, Hex.Decode(v192)))
 					{
-						mismatch("v192", v192, buf);
+						mismatch("v192/" + rounds, v192, buf);
 					}
 					break;
 				case 4:
 					if (!AreEqual(buf, Hex.Decode(v256)))
 					{
-						mismatch("v256", v256, buf);
+						mismatch("v256/" + rounds, v256, buf);
 					}
 					break;
 				default:
diff --git a/crypto/test/src/crypto/test/XSalsa20Test.cs b/crypto/test/src/crypto/test/XSalsa20Test.cs
new file mode 100644
index 000000000..74ed04e88
--- /dev/null
+++ b/crypto/test/src/crypto/test/XSalsa20Test.cs
@@ -0,0 +1,183 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+	/**
+	* XSalsa20 Test
+	*/
+	[TestFixture]
+	public class XSalsa20Test 
+		: SimpleTest
+	{
+		private class TestCase
+		{
+
+			private byte[] key;
+			private byte[] iv;
+			private byte[] plaintext;
+			private byte[] ciphertext;
+
+			public TestCase(String key, string iv, string plaintext, string ciphertext)
+			{
+				this.key = Hex.Decode(key);
+				this.iv = Hex.Decode(iv);
+				this.plaintext = Hex.Decode(plaintext);
+				this.ciphertext = Hex.Decode(ciphertext);
+			}
+
+			public byte[] Key
+			{
+				get { return key; }
+			}
+
+			public byte[] Iv
+			{
+				get { return iv; }
+			}
+
+			public byte[] Plaintext
+			{
+				get { return plaintext; }
+			}
+
+			public byte[] Ciphertext
+			{
+				get { return ciphertext; }
+			}
+		}
+
+		// Test cases generated by naclcrypto-20090308, as used by cryptopp
+		private static readonly TestCase[] TEST_CASES = new TestCase[] {
+			new TestCase(
+				"a6a7251c1e72916d11c2cb214d3c252539121d8e234e652d651fa4c8cff88030",
+				"9e645a74e9e0a60d8243acd9177ab51a1beb8d5a2f5d700c",
+				"093c5e5585579625337bd3ab619d615760d8c5b224a85b1d0efe0eb8a7ee163abb0376529fcc09bab506c618e13ce777d82c3ae9d1a6f972d4160287cbfe60bf2130fc0a6ff6049d0a5c8a82f429231f008082e845d7e189d37f9ed2b464e6b919e6523a8c1210bd52a02a4c3fe406d3085f5068d1909eeeca6369abc981a42e87fe665583f0ab85ae71f6f84f528e6b397af86f6917d9754b7320dbdc2fea81496f2732f532ac78c4e9c6cfb18f8e9bdf74622eb126141416776971a84f94d156beaf67aecbf2ad412e76e66e8fad7633f5b6d7f3d64b5c6c69ce29003c6024465ae3b89be78e915d88b4b5621d",
+				"b2af688e7d8fc4b508c05cc39dd583d6714322c64d7f3e63147aede2d9534934b04ff6f337b031815cd094bdbc6d7a92077dce709412286822ef0737ee47f6b7ffa22f9d53f11dd2b0a3bb9fc01d9a88f9d53c26e9365c2c3c063bc4840bfc812e4b80463e69d179530b25c158f543191cff993106511aa036043bbc75866ab7e34afc57e2cce4934a5faae6eabe4f221770183dd060467827c27a354159a081275a291f69d946d6fe28ed0b9ce08206cf484925a51b9498dbde178ddd3ae91a8581b91682d860f840782f6eea49dbb9bd721501d2c67122dea3b7283848c5f13e0c0de876bd227a856e4de593a3"),
+			new TestCase(
+				"9e1da239d155f52ad37f75c7368a536668b051952923ad44f57e75ab588e475a",
+				"af06f17859dffa799891c4288f6635b5c5a45eee9017fd72",
+				"feac9d54fc8c115ae247d9a7e919dd76cfcbc72d32cae4944860817cbdfb8c04e6b1df76a16517cd33ccf1acda9206389e9e318f5966c093cfb3ec2d9ee2de856437ed581f552f26ac2907609df8c613b9e33d44bfc21ff79153e9ef81a9d66cc317857f752cc175fd8891fefebb7d041e6517c3162d197e2112837d3bc4104312ad35b75ea686e7c70d4ec04746b52ff09c421451459fb59f",
+				"2c261a2f4e61a62e1b27689916bf03453fcbc97bb2af6f329391ef063b5a219bf984d07d70f602d85f6db61474e9d9f5a2deecb4fcd90184d16f3b5b5e168ee03ea8c93f3933a22bc3d1a5ae8c2d8b02757c87c073409052a2a8a41e7f487e041f9a49a0997b540e18621cad3a24f0a56d9b19227929057ab3ba950f6274b121f193e32e06e5388781a1cb57317c0ba6305e910961d01002f0"),
+			new TestCase("d5c7f6797b7e7e9c1d7fd2610b2abf2bc5a7885fb3ff78092fb3abe8986d35e2",
+	             "744e17312b27969d826444640e9c4a378ae334f185369c95",
+	             "7758298c628eb3a4b6963c5445ef66971222be5d1a4ad839715d1188071739b77cc6e05d5410f963a64167629757",
+	             "27b8cfe81416a76301fd1eec6a4d99675069b2da2776c360db1bdfea7c0aa613913e10f7a60fec04d11e65f2d64e"),
+			new TestCase(
+				"737d7811ce96472efed12258b78122f11deaec8759ccbd71eac6bbefa627785c",
+				"6fb2ee3dda6dbd12f1274f126701ec75c35c86607adb3edd",
+				"501325fb2645264864df11faa17bbd58312b77cad3d94ac8fb8542f0eb653ad73d7fce932bb874cb89ac39fc47f8267cf0f0c209f204b2d8578a3bdf461cb6a271a468bebaccd9685014ccbc9a73618c6a5e778a21cc8416c60ad24ddc417a130d53eda6dfbfe47d09170a7be1a708b7b5f3ad464310be36d9a2a95dc39e83d38667e842eb6411e8a23712297b165f690c2d7ca1b1346e3c1fccf5cafd4f8be0",
+				"6724c372d2e9074da5e27a6c54b2d703dc1d4c9b1f8d90f00c122e692ace7700eadca942544507f1375b6581d5a8fb39981c1c0e6e1ff2140b082e9ec016fce141d5199647d43b0b68bfd0fea5e00f468962c7384dd6129aea6a3fdfe75abb210ed5607cef8fa0e152833d5ac37d52e557b91098a322e76a45bbbcf4899e790618aa3f4c2e5e0fc3de93269a577d77a5502e8ea02f717b1dd2df1ec69d8b61ca"),
+			new TestCase(
+				"760158da09f89bbab2c99e6997f9523a95fcef10239bcca2573b7105f6898d34",
+				"43636b2cc346fc8b7c85a19bf507bdc3dafe953b88c69dba",
+				"d30a6d42dff49f0ed039a306bae9dec8d9e88366cc19e8c3642fd58fa0794ebf8029d949730339b0823a51f0f49f0d2c71f1051c1e0e2c86941f172789cdb1b0107413e70f982ff9761877bb526ef1c3eb1106a948d60ef21bd35d32cfd64f89b79ed63ecc5cca56246af736766f285d8e6b0da9cb1cd21020223ffacc5a32",
+				"c815b6b79b64f9369aec8dce8c753df8a50f2bc97c70ce2f014db33a65ac5816bac9e30ac08bdded308c65cb87e28e2e71b677dc25c5a6499c1553555daf1f55270a56959dffa0c66f24e0af00951ec4bb59ccc3a6c5f52e0981647e53e439313a52c40fa7004c855b6e6eb25b212a138e843a9ba46edb2a039ee82a263abe"),
+			new TestCase(
+				"27ba7e81e7edd4e71be53c07ce8e633138f287e155c7fa9e84c4ad804b7fa1b9",
+				"ea05f4ebcd2fb6b000da0612861ba54ff5c176fb601391aa",
+				"e09ff5d2cb050d69b2d42494bde5825238c756d6991d99d7a20d1ef0b83c371c89872690b2fc11d5369f4fc4971b6d3d6c078aef9b0f05c0e61ab89c025168054defeb03fef633858700c58b1262ce011300012673e893e44901dc18eee3105699c44c805897bdaf776af1833162a21a",
+				"a23e7ef93c5d0667c96d9e404dcbe6be62026fa98f7a3ff9ba5d458643a16a1cef7272dc6097a9b52f35983557c77a11b314b4f7d5dc2cca15ee47616f861873cbfed1d32372171a61e38e447f3cf362b3abbb2ed4170d89dcb28187b7bfd206a3e026f084a7e0ed63d319de6bc9afc0"),
+			new TestCase("6799d76e5ffb5b4920bc2768bafd3f8c16554e65efcf9a16f4683a7a06927c11",
+	             "61ab951921e54ff06d9b77f313a4e49df7a057d5fd627989", "472766", "8fd7df"),
+			new TestCase(
+				"f68238c08365bb293d26980a606488d09c2f109edafa0bbae9937b5cc219a49c",
+				"5190b51e9b708624820b5abdf4e40fad1fb950ad1adc2d26",
+				"47ec6b1f73c4b7ff5274a0bfd7f45f864812c85a12fbcb3c2cf8a3e90cf66ccf2eacb521e748363c77f52eb426ae57a0c6c78f75af71284569e79d1a92f949a9d69c4efc0b69902f1e36d7562765543e2d3942d9f6ff5948d8a312cff72c1afd9ea3088aff7640bfd265f7a9946e606abc77bcedae6bddc75a0dba0bd917d73e3bd1268f727e0096345da1ed25cf553ea7a98fea6b6f285732de37431561ee1b3064887fbcbd71935e02",
+				"36160e88d3500529ba4edba17bc24d8cfaca9a0680b3b1fc97cf03f3675b7ac301c883a68c071bc54acdd3b63af4a2d72f985e51f9d60a4c7fd481af10b2fc75e252fdee7ea6b6453190617dcc6e2fe1cd56585fc2f0b0e97c5c3f8ad7eb4f31bc4890c03882aac24cc53acc1982296526690a220271c2f6e326750d3fbda5d5b63512c831f67830f59ac49aae330b3e0e02c9ea0091d19841f1b0e13d69c9fbfe8a12d6f30bb734d9d2"),
+			new TestCase(
+				"45b2bd0de4ed9293ec3e26c4840faaf64b7d619d51e9d7a2c7e36c83d584c3df",
+				"546c8c5d6be8f90952cab3f36d7c1957baaa7a59abe3d7e5",
+				"5007c8cd5b3c40e17d7fe423a87ae0ced86bec1c39dc07a25772f3e96dabd56cd3fd7319f6c9654925f2d87087a700e1b130da796895d1c9b9acd62b266144067d373ed51e787498b03c52faad16bb3826fa511b0ed2a19a8663f5ba2d6ea7c38e7212e9697d91486c49d8a000b9a1935d6a7ff7ef23e720a45855481440463b4ac8c4f6e7062adc1f1e1e25d3d65a31812f58a71160",
+				"8eacfba568898b10c0957a7d44100685e8763a71a69a8d16bc7b3f88085bb9a2f09642e4d09a9f0ad09d0aad66b22610c8bd02ff6679bb92c2c026a216bf425c6be35fb8dae7ff0c72b0efd6a18037c70eed0ca90062a49a3c97fdc90a8f9c2ea536bfdc41918a7582c9927fae47efaa3dc87967b7887dee1bf071734c7665901d9105dae2fdf66b4918e51d8f4a48c60d19fbfbbcba"),
+			new TestCase(
+				"fe559c9a282beb40814d016d6bfcb2c0c0d8bf077b1110b8703a3ce39d70e0e1",
+				"b076200cc7011259805e18b304092754002723ebec5d6200",
+				"6db65b9ec8b114a944137c821fd606be75478d928366d5284096cdef782fcff7e8f59cb8ffcda979757902c5ffa6bc477ceaa4cb5d5ea76f94d91e833f823a6bc78f1055dfa6a97bea8965c1cde67a668e001257334a585727d9e0f7c1a06e88d3d25a4e6d9096c968bf138e116a3ebeffd4bb4808adb1fd698164ba0a35c709a47f16f1f4435a2345a9194a00b95abd51851d505809a6077da9baca5831afff31578c487ee68f2767974a98a7e803aac788da98319c4ea8eaa3d394855651f484cef543f537e35158ee29",
+				"4dce9c8f97a028051b0727f34e1b9ef21f06f0760f36e71713204027902090ba2bb6b13436ee778d9f50530efbd7a32b0d41443f58ccaee781c7b716d3a96fdec0e3764ed7959f34c3941278591ea033b5cbadc0f1916032e9bebbd1a8395b83fb63b1454bd775bd20b3a2a96f951246ac14daf68166ba62f6cbff8bd121ac9498ff8852fd2be975df52b5daef3829d18eda42e715022dcbf930d0a789ee6a146c2c7088c35773c63c06b4af4559856ac199ced86863e4294707825337c5857970eb7fddeb263781309011"),
+			new TestCase(
+				"0ae10012d7e56614b03dcc89b14bae9242ffe630f3d7e35ce8bbb97bbc2c92c3",
+				"f96b025d6cf46a8a12ac2af1e2aef1fb83590adadaa5c5ea",
+				"ea0f354e96f12bc72bbaa3d12b4a8ed879b042f0689878f46b651cc4116d6f78409b11430b3aaa30b2076891e8e1fa528f2fd169ed93dc9f84e24409eec2101daf4d057be2492d11de640cbd7b355ad29fb70400fffd7cd6d425abeeb732a0eaa4330af4c656252c4173deab653eb85c58462d7ab0f35fd12b613d29d473d330310dc323d3c66348bbdbb68a326324657cae7b77a9e34358f2cec50c85609e73056856796e3be8d62b6e2fe9f953",
+				"e8abd48924b54e5b80866be7d4ebe5cf4274cafff08b39cb2d40a8f0b472398aedc776e0793812fbf1f60078635d2ed86b15efcdba60411ee23b07233592a44ec31b1013ce8964236675f8f183aef885e864f2a72edf4215b5338fa2b54653dfa1a8c55ce5d95cc605b9b311527f2e3463ffbec78a9d1d65dabad2f338769c9f43f133a791a11c7eca9af0b771a4ac32963dc8f631a2c11217ac6e1b9430c1aae1ceebe22703f429998a8fb8c641"),
+			new TestCase(
+				"082c539bc5b20f97d767cd3f229eda80b2adc4fe49c86329b5cd6250a9877450",
+				"845543502e8b64912d8f2c8d9fffb3c69365686587c08d0c",
+				"a96bb7e910281a6dfad7c8a9c370674f0ceec1ad8d4f0de32f9ae4a23ed329e3d6bc708f876640a229153ac0e7281a8188dd77695138f01cda5f41d5215fd5c6bdd46d982cb73b1efe2997970a9fdbdb1e768d7e5db712068d8ba1af6067b5753495e23e6e1963af012f9c7ce450bf2de619d3d59542fb55f3",
+				"835da74fc6de08cbda277a7966a07c8dcd627e7b17adde6d930b6581e3124b8baad096f693991fedb1572930601fc7709541839b8e3ffd5f033d2060d999c6c6e3048276613e648000acb5212cc632a916afce290e20ebdf612d08a6aa4c79a74b070d3f872a861f8dc6bb07614db515d363349d3a8e3336a3"),
+			new TestCase("3d02bff3375d403027356b94f514203737ee9a85d2052db3e4e5a217c259d18a",
+	             "74216c95031895f48c1dba651555ebfa3ca326a755237025",
+	             "0d4b0f54fd09ae39baa5fa4baccf2e6682e61b257e01f42b8f",
+	             "16c4006c28365190411eb1593814cf15e74c22238f210afc3d"),
+			new TestCase(
+				"ad1a5c47688874e6663a0f3fa16fa7efb7ecadc175c468e5432914bdb480ffc6",
+				"e489eed440f1aae1fac8fb7a9825635454f8f8f1f52e2fcc",
+				"aa6c1e53580f03a9abb73bfdadedfecada4c6b0ebe020ef10db745e54ba861caf65f0e40dfc520203bb54d29e0a8f78f16b3f1aa525d6bfa33c54726e59988cfbec78056",
+				"02fe84ce81e178e7aabdd3ba925a766c3c24756eefae33942af75e8b464556b5997e616f3f2dfc7fce91848afd79912d9fb55201b5813a5a074d2c0d4292c1fd441807c5"),
+			new TestCase(
+				"053a02bedd6368c1fb8afc7a1b199f7f7ea2220c9a4b642a6850091c9d20ab9c",
+				"c713eea5c26dad75ad3f52451e003a9cb0d649f917c89dde",
+				"8f0a8a164760426567e388840276de3f95cb5e3fadc6ed3f3e4fe8bc169d9388804dcb94b6587dbb66cb0bd5f87b8e98b52af37ba290629b858e0e2aa7378047a26602",
+				"516710e59843e6fbd4f25d0d8ca0ec0d47d39d125e9dad987e0518d49107014cb0ae405e30c2eb3794750bca142ce95e290cf95abe15e822823e2e7d3ab21bc8fbd445"),
+			new TestCase(
+				"5b14ab0fbed4c58952548a6cb1e0000cf4481421f41288ea0aa84add9f7deb96",
+				"54bf52b911231b952ba1a6af8e45b1c5a29d97e2abad7c83",
+				"37fb44a675978b560ff9a4a87011d6f3ad2d37a2c3815b45a3c0e6d1b1d8b1784cd468927c2ee39e1dccd4765e1c3d676a335be1ccd6900a45f5d41a317648315d8a8c24adc64eb285f6aeba05b9029586353d303f17a807658b9ff790474e1737bd5fdc604aeff8dfcaf1427dcc3aacbb0256badcd183ed75a2dc52452f87d3c1ed2aa583472b0ab91cda20614e9b6fdbda3b49b098c95823cc72d8e5b717f2314b0324e9ce",
+				"ae6deb5d6ce43d4b09d0e6b1c0e9f46157bcd8ab50eaa3197ff9fa2bf7af649eb52c68544fd3adfe6b1eb316f1f23538d470c30dbfec7e57b60cbcd096c782e7736b669199c8253e70214cf2a098fda8eac5da79a9496a3aae754d03b17c6d70d1027f42bf7f95ce3d1d9c338854e158fcc803e4d6262fb639521e47116ef78a7a437ca9427ba645cd646832feab822a208278e45e93e118d780b988d65397eddfd7a819526e"),
+			new TestCase(
+				"d74636e3413a88d85f322ca80fb0bd650bd0bf0134e2329160b69609cd58a4b0",
+				"efb606aa1d9d9f0f465eaa7f8165f1ac09f5cb46fecf2a57",
+				"f85471b75f6ec81abac2799ec09e98e280b2ffd64ca285e5a0109cfb31ffab2d617b2c2952a2a8a788fc0da2af7f530758f74f1ab56391ab5ff2adbcc5be2d6c7f49fbe8118104c6ff9a23c6dfe52f57954e6a69dcee5db06f514f4a0a572a9a8525d961dae72269b987189d465df6107119c7fa790853e063cba0fab7800ca932e258880fd74c33c784675bedad0e7c09e9cc4d63dd5e9713d5d4a0196e6b562226ac31b4f57c04f90a181973737ddc7e80f364112a9fbb435ebdbcabf7d490ce52",
+				"b2b795fe6c1d4c83c1327e015a67d4465fd8e32813575cbab263e20ef05864d2dc17e0e4eb81436adfe9f638dcc1c8d78f6b0306baf938e5d2ab0b3e05e735cc6fff2d6e02e3d60484bea7c7a8e13e23197fea7b04d47d48f4a4e5944174539492800d3ef51e2ee5e4c8a0bdf050c2dd3dd74fce5e7e5c37364f7547a11480a3063b9a0a157b15b10a5a954de2731ced055aa2e2767f0891d4329c426f3808ee867bed0dc75b5922b7cfb895700fda016105a4c7b7f0bb90f029f6bbcb04ac36ac16") };
+
+		public override string Name
+		{
+			get { return "XSalsa20"; }
+		}
+
+		public override void PerformTest()
+		{
+			for (int i = 0; i < TEST_CASES.Length; i++)
+			{
+				performTest(i, TEST_CASES[i]);
+			}
+		}
+
+		private void performTest(int number, TestCase testCase)
+		{
+			byte[] plaintext = testCase.Plaintext;
+			byte[] output = new byte[plaintext.Length];
+
+			XSalsa20Engine engine = new XSalsa20Engine();
+			engine.Init(false, new ParametersWithIV(new KeyParameter(testCase.Key), testCase.Iv));
+
+			engine.ProcessBytes(testCase.Plaintext, 0, testCase.Plaintext.Length, output, 0);
+
+			if (!Arrays.AreEqual(testCase.Ciphertext, output))
+			{
+				Fail("mismatch on " + number, Hex.ToHexString(testCase.Ciphertext), Hex.ToHexString(output));
+			}
+		}
+
+		public static void Main(
+			string[] args)
+		{
+			RunTest(new XSalsa20Test());
+		}
+
+		[Test]
+		public void TestFunction()
+		{
+			string resultText = Perform().ToString();
+
+			Assert.AreEqual(Name + ": Okay", resultText);
+		}
+	}
+}