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);
+ }
+ }
+}
|