summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2013-11-01 18:46:29 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2013-11-01 18:46:29 +0700
commitc1203484971419bc08b1ae937ee439aef9161b4d (patch)
tree8120ae47d99573e9ccbe988445a68279218449c3 /crypto/src
parentadded obligatory Inc. (diff)
parentUse xmldoc for documentation of Salsa20/XSalsa20/ChaCha (diff)
downloadBouncyCastle.NET-ed25519-c1203484971419bc08b1ae937ee439aef9161b4d.tar.xz
Merge branch 'feature/latin-dances' of git://github.com/timw/bc-csharp
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/crypto/engines/ChaChaEngine.cs189
-rw-r--r--crypto/src/crypto/engines/Salsa20Engine.cs277
-rw-r--r--crypto/src/crypto/engines/XSalsa20Engine.cs71
3 files changed, 435 insertions, 102 deletions
diff --git a/crypto/src/crypto/engines/ChaChaEngine.cs b/crypto/src/crypto/engines/ChaChaEngine.cs
new file mode 100644
index 000000000..f4a7b8fe1
--- /dev/null
+++ b/crypto/src/crypto/engines/ChaChaEngine.cs
@@ -0,0 +1,189 @@
+using System;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+	/// <summary>
+	/// Implementation of Daniel J. Bernstein's ChaCha stream cipher.
+	/// </summary>
+	public class ChaChaEngine
+		: Salsa20Engine
+	{
+
+		/// <summary>
+		/// Creates a 20 rounds ChaCha engine.
+		/// </summary>
+		public ChaChaEngine()
+		{
+		}
+
+		/// <summary>
+		/// Creates a ChaCha engine with a specific number of rounds.
+		/// </summary>
+		/// <param name="rounds">the number of rounds (must be an even number).</param>
+		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);
+		}
+
+		/// <summary>
+		/// ChacCha function.
+		/// </summary>
+		/// <param name="rounds">The number of ChaCha rounds to execute</param>
+		/// <param name="input">The input words.</param>
+		/// <param name="x">The ChaCha state to modify.</param>
+		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..81884d603 100644
--- a/crypto/src/crypto/engines/Salsa20Engine.cs
+++ b/crypto/src/crypto/engines/Salsa20Engine.cs
@@ -7,44 +7,60 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
 {
-	/**
-	 * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
-	 */
+	/// <summary>
+	/// Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
+	/// </summary>
 	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;
+		internal uint[] engineState = new uint[StateSize]; // state
+		internal uint[] x = new uint[StateSize]; // internal buffer
+		private byte[]	 keyStream = new byte[StateSize * 4]; // expanded state, 64 bytes
+		private bool	 initialised = false;
 
 		/*
 		 * internal counter
 		 */
 		private uint cW0, cW1, cW2;
 
-		/**
-		 * initialise a Salsa20 cipher.
-		 *
-		 * @param forEncryption whether or not we are for encryption.
-		 * @param params the parameters required to set up the cipher.
-		 * @exception ArgumentException if the params argument is
-		 * inappropriate.
-		 */
+		/// <summary>
+		/// Creates a 20 round Salsa20 engine.
+		/// </summary>
+		public Salsa20Engine()
+			: this(DEFAULT_ROUNDS)
+		{
+		}
+
+		/// <summary>
+		/// Creates a Salsa20 engine with a specific number of rounds.
+		/// </summary>
+		/// <param name="rounds">the number of rounds (must be an even number).</param>
+		public Salsa20Engine(int rounds)
+		{
+			if (rounds <= 0 || (rounds & 1) != 0)
+			{
+				throw new ArgumentException("'rounds' must be a positive, even number");
+			}
+
+			this.rounds = rounds;
+		}
+
 		public void Init(
 			bool				forEncryption, 
 			ICipherParameters	parameters)
@@ -58,27 +74,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 +119,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 +128,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 +168,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 +177,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 +212,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)
+		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
+		 */
+		internal 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..fc6630905
--- /dev/null
+++ b/crypto/src/crypto/engines/XSalsa20Engine.cs
@@ -0,0 +1,71 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+	/// <summary>
+	/// Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce.
+	/// </summary>
+	/// <remarks>
+	/// XSalsa20 requires a 256 bit key, and a 192 bit nonce.
+	/// </remarks>
+	public class XSalsa20Engine 
+		: Salsa20Engine
+	{
+
+		public override string AlgorithmName
+		{
+			get { return "XSalsa20"; }
+		}
+
+		protected override int NonceSize
+		{
+			get { return 24; }
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		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();
+		}
+
+	}
+}
+