summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2016-01-17 14:02:18 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2016-01-17 14:02:18 +0700
commit715d196d48d494f7ab2f05c5f8a06bc554d9dbc3 (patch)
tree1ff49e296ba78946a485a0cd94288959664e3575
parentFix re-init bug in HC128/256 engines (diff)
downloadBouncyCastle.NET-ed25519-715d196d48d494f7ab2f05c5f8a06bc554d9dbc3.tar.xz
Support for re-init in Salsa family
- simplify SetKey methods
- avoid non-private mutable static state (sigma/tau)
-rw-r--r--crypto/src/crypto/engines/ChaChaEngine.cs63
-rw-r--r--crypto/src/crypto/engines/Salsa20Engine.cs115
-rw-r--r--crypto/src/crypto/engines/XSalsa20Engine.cs19
-rw-r--r--crypto/src/crypto/util/Pack.cs11
4 files changed, 93 insertions, 115 deletions
diff --git a/crypto/src/crypto/engines/ChaChaEngine.cs b/crypto/src/crypto/engines/ChaChaEngine.cs
index 46b59ed2e..8720504cd 100644
--- a/crypto/src/crypto/engines/ChaChaEngine.cs
+++ b/crypto/src/crypto/engines/ChaChaEngine.cs
@@ -1,4 +1,5 @@
 using System;
+
 using Org.BouncyCastle.Crypto.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
@@ -9,7 +10,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 	public class ChaChaEngine
 		: Salsa20Engine
 	{
-
 		/// <summary>
 		/// Creates a 20 rounds ChaCha engine.
 		/// </summary>
@@ -46,45 +46,20 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 		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;
-			}
+            if (keyBytes != null)
+            {
+                if ((keyBytes.Length != 16) && (keyBytes.Length != 32))
+                    throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key");
 
-			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);
+                PackTauOrSigma(keyBytes.Length, engineState, 0);
 
-			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;
+                // Key
+                Pack.LE_To_UInt32(keyBytes, 0, engineState, 4, 4);
+                Pack.LE_To_UInt32(keyBytes, keyBytes.Length - 16, engineState, 8, 4);
+            }
 
-			// IV
-			engineState[14] = Pack.LE_To_UInt32(ivBytes, 0);
-			engineState[15] = Pack.LE_To_UInt32(ivBytes, 4);
+            // IV
+            Pack.LE_To_UInt32(ivBytes, 0, engineState, 14, 2);
 		}
 
 		protected override void GenerateKeyStream(byte[] output)
@@ -94,24 +69,21 @@ namespace Org.BouncyCastle.Crypto.Engines
 		}
 
 		/// <summary>
-		/// ChacCha function.
+		/// ChaCha 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) {
+			if (input.Length != 16)
 				throw new ArgumentException();
-			}
-			if (x.Length != 16) {
+			if (x.Length != 16)
 				throw new ArgumentException();
-			}
-			if (rounds % 2 != 0) {
+			if (rounds % 2 != 0)
 				throw new ArgumentException("Number of rounds must be even");
-			}
 
-			uint x00 = input[ 0];
+            uint x00 = input[ 0];
 			uint x01 = input[ 1];
 			uint x02 = input[ 2];
 			uint x03 = input[ 3];
@@ -183,4 +155,3 @@ namespace Org.BouncyCastle.Crypto.Engines
 		}
 	}
 }
-
diff --git a/crypto/src/crypto/engines/Salsa20Engine.cs b/crypto/src/crypto/engines/Salsa20Engine.cs
index 9b27dc7b4..182eacd71 100644
--- a/crypto/src/crypto/engines/Salsa20Engine.cs
+++ b/crypto/src/crypto/engines/Salsa20Engine.cs
@@ -18,7 +18,19 @@ namespace Org.BouncyCastle.Crypto.Engines
 		/** Constants */
 		private const int StateSize = 16; // 16, 32 bit ints = 64 bytes
 
-		protected readonly static byte[]
+        private readonly static uint[] TAU_SIGMA = Pack.LE_To_UInt32(Strings.ToAsciiByteArray("expand 16-byte k" + "expand 32-byte k"), 0, 8);
+
+        internal void PackTauOrSigma(int keyLength, uint[] state, int stateOffset)
+        {
+            int tsOff = (keyLength - 16) / 4;
+            state[stateOffset] = TAU_SIGMA[tsOff];
+            state[stateOffset + 1] = TAU_SIGMA[tsOff + 1];
+            state[stateOffset + 2] = TAU_SIGMA[tsOff + 2];
+            state[stateOffset + 3] = TAU_SIGMA[tsOff + 3];
+        }
+
+        [Obsolete]
+        protected readonly static byte[]
 			sigma = Strings.ToAsciiByteArray("expand 32-byte k"),
 			tau = Strings.ToAsciiByteArray("expand 16-byte k");
 
@@ -72,22 +84,31 @@ namespace Org.BouncyCastle.Crypto.Engines
 			 */
 
 			ParametersWithIV ivParams = parameters as ParametersWithIV;
-
 			if (ivParams == null)
 				throw new ArgumentException(AlgorithmName + " Init requires an IV", "parameters");
 
 			byte[] iv = ivParams.GetIV();
-
 			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(AlgorithmName + " Init requires a key", "parameters");
-
-			SetKey(key.GetKey(), iv);
-			Reset();
+            ICipherParameters keyParam = ivParams.Parameters;
+            if (keyParam == null)
+            {
+                if (!initialised)
+                    throw new InvalidOperationException(AlgorithmName + " KeyParameter can not be null for first initialisation");
+
+                SetKey(null, iv);
+            }
+            else if (keyParam is KeyParameter)
+            {
+                SetKey(((KeyParameter)keyParam).GetKey(), iv);
+            }
+            else
+            {
+                throw new ArgumentException(AlgorithmName + " Init parameters must contain a KeyParameter (or null for re-init)");
+            }
+
+            Reset();
 			initialised = true;
 		}
 
@@ -98,7 +119,8 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 		public virtual string AlgorithmName
 		{
-			get { 
+			get
+            { 
 				string name = "Salsa20";
 				if (rounds != DEFAULT_ROUNDS)
 				{
@@ -178,45 +200,27 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 		protected virtual 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[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 (keyBytes.Length == 32)
-			{
-				constants = sigma;
-				offset = 16;
-			}
-			else
-			{
-				constants = tau;
-			}
-
-			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(ivBytes, 0);
-			engineState[7] = Pack.LE_To_UInt32(ivBytes, 4);
-			ResetCounter();
-		}
-
-		protected virtual void GenerateKeyStream(byte[] output)
+            if (keyBytes != null)
+            {
+                if ((keyBytes.Length != 16) && (keyBytes.Length != 32))
+                    throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key");
+
+                int tsOff = (keyBytes.Length - 16) / 4;
+                engineState[0] = TAU_SIGMA[tsOff];
+                engineState[5] = TAU_SIGMA[tsOff + 1];
+                engineState[10] = TAU_SIGMA[tsOff + 2];
+                engineState[15] = TAU_SIGMA[tsOff + 3];
+
+                // Key
+                Pack.LE_To_UInt32(keyBytes, 0, engineState, 1, 4);
+                Pack.LE_To_UInt32(keyBytes, keyBytes.Length - 16, engineState, 11, 4);
+            }
+
+            // IV
+            Pack.LE_To_UInt32(ivBytes, 0, engineState, 6, 2);
+        }
+
+        protected virtual void GenerateKeyStream(byte[] output)
 		{
 			SalsaCore(rounds, engineState, x);
 			Pack.UInt32_To_LE(x, output, 0);
@@ -224,17 +228,14 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 		internal static void SalsaCore(int rounds, uint[] input, uint[] x)
 		{
-			if (input.Length != 16) {
+			if (input.Length != 16)
 				throw new ArgumentException();
-			}
-			if (x.Length != 16) {
+			if (x.Length != 16)
 				throw new ArgumentException();
-			}
-			if (rounds % 2 != 0) {
+			if (rounds % 2 != 0)
 				throw new ArgumentException("Number of rounds must be even");
-			}
 
-			uint x00 = input[ 0];
+            uint x00 = input[ 0];
 			uint x01 = input[ 1];
 			uint x02 = input[ 2];
 			uint x03 = input[ 3];
diff --git a/crypto/src/crypto/engines/XSalsa20Engine.cs b/crypto/src/crypto/engines/XSalsa20Engine.cs
index 2898b46c8..50c51a82f 100644
--- a/crypto/src/crypto/engines/XSalsa20Engine.cs
+++ b/crypto/src/crypto/engines/XSalsa20Engine.cs
@@ -30,17 +30,17 @@ namespace Org.BouncyCastle.Crypto.Engines
 		/// </summary>
 		protected override void SetKey(byte[] keyBytes, byte[] ivBytes)
 		{
-			if (keyBytes.Length != 32)
-			{
+            if (keyBytes == null)
+                throw new ArgumentException(AlgorithmName + " doesn't support re-init with null key");
+
+            if (keyBytes.Length != 32)
 				throw new ArgumentException(AlgorithmName + " requires a 256 bit key");
-			}
 
-			// Set key for HSalsa20
+            // 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);
+            Pack.LE_To_UInt32(ivBytes, 8, engineState, 8, 2);
 
 			// Process engine state to generate Salsa20 key
 			uint[] hsalsa20Out = new uint[engineState.Length];
@@ -58,12 +58,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			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();
+            Pack.LE_To_UInt32(ivBytes, 16, engineState, 6, 2);
 		}
 	}
 }
-
diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs
index dc00ab450..96f293d72 100644
--- a/crypto/src/crypto/util/Pack.cs
+++ b/crypto/src/crypto/util/Pack.cs
@@ -255,6 +255,17 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        internal static uint[] LE_To_UInt32(byte[] bs, int off, int count)
+        {
+            uint[] ns = new uint[count];
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                ns[i] = LE_To_UInt32(bs, off);
+                off += 4;
+            }
+            return ns;
+        }
+
         internal static byte[] UInt64_To_LE(ulong n)
         {
             byte[] bs = new byte[8];