summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorTim Whittington <bc@whittington.net.nz>2013-10-12 19:54:12 +1300
committerTim Whittington <bc@whittington.net.nz>2013-10-20 21:32:35 +1300
commitec5ba47d35551b3ef8572770d120cc418648e904 (patch)
tree7afc6ad1ac2b5f992cc6bfe53d5c5de513acc508 /crypto
parentPort Memoable digest support from bc-java. (diff)
downloadBouncyCastle.NET-ed25519-ec5ba47d35551b3ef8572770d120cc418648e904.tar.xz
Port SM3 digest implementation and tests from bc-java.
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto.mdp2
-rw-r--r--crypto/src/crypto/digests/Sm3Digest.cs328
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs1
-rw-r--r--crypto/test/src/crypto/test/Sm3DigestTest.cs74
4 files changed, 405 insertions, 0 deletions
diff --git a/crypto/crypto.mdp b/crypto/crypto.mdp
index e79b81b17..0e2349801 100644
--- a/crypto/crypto.mdp
+++ b/crypto/crypto.mdp
@@ -2310,6 +2310,8 @@
     <File subtype="Code" buildaction="Compile" name="src/crypto/macs/SkeinMac.cs" />
     <File subtype="Code" buildaction="Compile" name="src/util/IMemoable.cs" />
     <File subtype="Code" buildaction="Compile" name="src/util/MemoableResetException.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/crypto/digests/Sm3Digest.cs" />
+    <File subtype="Code" buildaction="Compile" name="test/src/crypto/test/Sm3DigestTest.cs" />
   </Contents>
   <References>
     <ProjectReference type="Assembly" localcopy="True" refto="test/lib/nunit.core.dll" />
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/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index 576ae5ee4..a86e311ee 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -115,6 +115,7 @@ namespace Org.BouncyCastle.Crypto.Tests
             new NullTest(),
             new SipHashTest(),
             new OcbTest(),
+			new Sm3DigestTest()
         };
 
         public static void Main(
diff --git a/crypto/test/src/crypto/test/Sm3DigestTest.cs b/crypto/test/src/crypto/test/Sm3DigestTest.cs
new file mode 100644
index 000000000..3d004deaa
--- /dev/null
+++ b/crypto/test/src/crypto/test/Sm3DigestTest.cs
@@ -0,0 +1,74 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+	/**
+	 * standard vector test for SM3 digest from chinese specification
+	 */
+	[TestFixture]
+	public class Sm3DigestTest
+	    : DigestTest
+	{
+	    private static string[] messages = {
+	        // Standard test vectors
+	        "abc",
+	        "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
+	        // Non-standard test vectors
+	        "",
+	        "a",
+	        "abcdefghijklmnopqrstuvwxyz",
+	    };
+
+	    private static string[] digests = {
+	        // Standard test vectors
+	        "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0",
+	        "debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732",
+	        // Non-standard test vectors
+	        "1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b",
+	        "623476ac18f65a2909e43c7fec61b49c7e764a91a18ccb82f1917a29c86c5e88",
+	        "b80fe97a4da24afc277564f66a359ef440462ad28dcc6d63adb24d5c20a61595",
+	    };
+
+	    private static string sixtyFourKdigest = "97049bdc8f0736bc7300eafa9980aeb9cf00f24f7ec3a8f1f8884954d7655c1d";
+	    private static string million_a_digest = "c8aaf89429554029e231941a2acc0ad61ff2a5acd8fadd25847a3a732b3b02c3";
+
+	    internal Sm3DigestTest()
+			: base(new Sm3Digest(), messages, digests)
+	    {
+	    }
+
+	    public override void PerformTest()
+	    {
+	        base.PerformTest();
+
+	        sixtyFourKTest(sixtyFourKdigest);
+	        millionATest(million_a_digest);
+	    }
+
+	    protected override IDigest CloneDigest(IDigest digest)
+	    {
+	        return new Sm3Digest((Sm3Digest)digest);
+	    }
+
+		public static void Main(
+			string[] args)
+		{
+			RunTest(new Sm3DigestTest());
+		}
+
+		[Test]
+		public void TestFunction()
+		{
+			string resultText = Perform().ToString();
+
+			Assert.AreEqual(Name + ": Okay", resultText);
+		}
+	}
+}