国家商用密码(二)C#:基于BouncyCastle实现SM3密码杂凑算法

同系列文章:

/* ----------------------------------------------------------
 * 文件名称:SM3.cs
 * 
 * 作者:秦建辉
 * 
 * QQ:36748897
 * 
 * 博客:http://www.firstsolver.com/wordpress/
 * 
 * 开发环境:
 *      Visual Studio V2013
 *      .NET Framework 4.5
 *      BouncyCastle Release 1.7
 *      
 * 版本历史:
 *      V1.0    2015年05月28日
 *              基于BouncyCastle 实现SM3密码杂凑算法
 *
 *  * 参考资料:
 *	http://www.oscca.gov.cn/UpFile/20101222141857786.pdf
 *
 * 说明:
 *	SM3生成256位散列值
 *
 * 测试数据
 * 输入:"abc"
 * 哈希值:66C7F0F4 62EEEDD9 D1F2D46B DC10E4E2 4167C487 5CF2F7A2 297DA02B 8F4BA8E0
 *
 * 输入:"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"
 * 哈希值:DEBE9FF9 2275B8A1 38604889 C18E5A4D 6FDB70E5 387E5765 293DCBA3 9C0C5732
 * ------------------------------------------------------------ */
using Org.BouncyCastle.Crypto;
using System;

namespace Splash
{
    /// <summary>
    /// SM3密码杂凑算法
    /// </summary>
    public class SM3 : IDigest
    {
        /// <summary>
        /// 杂凑值长度(字节数)
        /// </summary>
        public const int SM3_HASH_BYTES = 32;

        /// <summary>
        /// 分组长度(字节数)
        /// </summary>
        public const int SM3_InputBlockSize = 64;

        /// <summary>
        /// 初始状态向量
        /// </summary>
        private static readonly UInt32[] IV = { 0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600, 0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E };

        /// <summary>
        /// 填充数据
        /// </summary>
        private static readonly byte[] SM3_PADDING = {
            0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        };

        /// <summary>
        /// 当前状态向量
        /// </summary>
        private UInt32[] V = new UInt32[SM3_HASH_BYTES >> 2];

        /// <summary>
        /// 内部数据缓冲区
        /// </summary>
        private UInt32[] W = new UInt32[68];

        /// <summary>
        /// 4字节数据单元存储器
        /// </summary>
        private byte[] M = new byte[4];

        /// <summary>
        /// 已处理的字节计数
        /// </summary>
        private UInt64 BytesCount = 0;   

        /// <summary>
        /// 缓冲区X存储位置偏移量
        /// </summary>
        private int XOff = 0;

        /// <summary>
        /// 缓冲区M存储位置偏移量
        /// </summary>
        private int MOff = 0;

        /// <summary>
        /// 构造函数
        /// </summary>
        public SM3()
		{
			Reset();
		}

        /// <summary>
        /// 拷贝构造函数
        /// </summary>
        /// <param name="t">要复制的对象实例</param>
        public SM3(SM3 t)
		{
            BytesCount = t.BytesCount;

            XOff = t.XOff;
            if (XOff > 0) Array.Copy(t.W, W, XOff);

            MOff = t.MOff;
            if (MOff > 0) Array.Copy(t.M, M, MOff);
            
            // 拷贝状态向量
			Array.Copy(t.V, V, V.Length);
		}

        /// <summary>
        /// 恢复到初始状态
        /// </summary>
        public void Reset()
        {
            BytesCount = 0;
            XOff = 0;
            MOff = 0;
            Array.Copy(IV, V, V.Length);

            // 清除敏感数据
            Array.Clear(W, 0, W.Length);
        }

        /// <summary>
        /// 密码杂凑算法名称
        /// </summary>
        public string AlgorithmName
        {
            get { return "SM3"; }
        }

        /// <summary>
        /// 内部缓冲区大小
        /// </summary>
        /// <returns>内部缓冲区大小</returns>
        public int GetByteLength()
        {
            return SM3_InputBlockSize;
        }

        /// <summary>
        /// 杂凑值长度,256比特,32字节
        /// </summary>
        /// <returns>杂凑值长度</returns>
        public int GetDigestSize()
        {
            return SM3_HASH_BYTES;
        }

        /// <summary>
        /// 处理单个数据
        /// </summary>
        /// <param name="input">输入的单字节数据</param>
        public void Update(byte input)
        {
            M[MOff++] = input;

            if (MOff == 4)
            {
                ProcessWord(M, 0);
                MOff = 0;
            }

            BytesCount++;
        }

        /// <summary>
        /// 处理批量数据
        /// </summary>
        /// <param name="input">包含输入数据的字节数组</param>
        /// <param name="inOff">数据在字节数组中的起始偏移量</param>
        /// <param name="length">数据长度</param>
        public void BlockUpdate(byte[] input, int inOff, int length)
        {
            while ((MOff != 0) && (length > 0))
            {
                Update(input[inOff]);
                inOff++;
                length--;
            }

            while (length >= 4)
            {
                ProcessWord(input, inOff);

                inOff += 4;
                length -= 4;
                BytesCount += 4;
            }

            while (length > 0)
            {
                Update(input[inOff]);

                inOff++;
                length--;
            }
        }

        /// <summary>
        /// 获取最终的杂凑值
        /// </summary>
        /// <param name="output">用于存储最终杂凑值的字节数组</param>
        /// <param name="outOff">存放数据的起始位置</param>
        /// <returns>写入数据的长度</returns>
        public int DoFinal(byte[] output, int outOff)
        {
            Finish();

            foreach(UInt32 n in V)
            {
                output[outOff++] = (byte)(n >> 24);
                output[outOff++] = (byte)(n >> 16);
                output[outOff++] = (byte)(n >> 8);
                output[outOff++] = (byte)(n & 0xFF);
            }

            Reset();
            return SM3_HASH_BYTES;
        }

        private void ProcessWord(byte[] input, int inOff)
        {
            W[XOff++] = (UInt32)((input[inOff] << 24) | (input[inOff + 1] << 16) | (input[inOff + 2] << 8) | input[inOff + 3]);
            if (XOff == 16)
            {
                ProcessBlock();
            }
        }

        private void ProcessBlock()
        {
            UInt32[] W1 = new UInt32[64];

            // 消息扩展(生成132个4字节数据)
            // a:将消息分组B(i)划分为16个4字节整型数据
            
            // b:W[j] = P1(W[j-16] ^ W[j-9] ^ ROTL(W[j-3],15)) ^ ROTL(W[j - 13],7) ^ W[j-6]
            for (int j = 16; j < 68; j++)
            {
                W[j] = P1(W[j - 16] ^ W[j - 9] ^ ROTL(W[j - 3], 15)) ^ ROTL(W[j - 13], 7) ^ W[j - 6];
            }

            // c:W1[j] = W[j] ^ W[j+4]
            for (int j = 0; j < 64; j++)
            {
                W1[j] = W[j] ^ W[j + 4];
            }

            // 压缩函数
            UInt32 A = V[0];
            UInt32 B = V[1];
            UInt32 C = V[2];
            UInt32 D = V[3];
            UInt32 E = V[4];
            UInt32 F = V[5];
            UInt32 G = V[6];
            UInt32 H = V[7];
            
            for (int j = 0; j < 16; j++)
            {
                UInt32 Q = ROTL(A, 12);
                UInt32 SS1 = ROTL((Q + E + ROTL(0x79CC4519, j)), 7);
                UInt32 SS2 = SS1 ^ Q;
                UInt32 TT1 = FF0(A, B, C) + D + SS2 + W1[j];
                UInt32 TT2 = GG0(E, F, G) + H + SS1 + W[j];
                D = C;
                C = ROTL(B, 9);
                B = A;
                A = TT1;
                H = G;
                G = ROTL(F, 19);
                F = E;
                E = P0(TT2);
            }

            for (int j = 16; j < 64; j++)
            {
                UInt32 Q = ROTL(A, 12);
                UInt32 SS1 = ROTL((Q + E + ROTL(0x7A879D8A, j)), 7);
                UInt32 SS2 = SS1 ^ Q;
                UInt32 TT1 = FF1(A, B, C) + D + SS2 + W1[j];
                UInt32 TT2 = GG1(E, F, G) + H + SS1 + W[j];
                D = C;
                C = ROTL(B, 9);
                B = A;
                A = TT1;
                H = G;
                G = ROTL(F, 19);
                F = E;
                E = P0(TT2);
            }

            V[0] ^= A;
            V[1] ^= B;
            V[2] ^= C;
            V[3] ^= D;
            V[4] ^= E;
            V[5] ^= F;
            V[6] ^= G;
            V[7] ^= H;

            // 重置缓冲区
            XOff = 0;
        }

        private void Finish()
        {
            // 计算实际消息比特长度
            UInt64 BitsLength = BytesCount << 3;

            // 计算填充字节数
            int LeftBytes = (XOff << 2) + MOff;
            int PaddingBytes = (LeftBytes < 56) ? (56 - LeftBytes) : (120 - LeftBytes);

            // 加入填充数据块
            BlockUpdate(SM3_PADDING, 0, PaddingBytes);   
         
            // 加入实际消息比特长度
            byte[] L = BitConverter.GetBytes(BitsLength);
            Array.Reverse(L);
            BlockUpdate(L, 0, 8);
        }

        // 四字节无符号整数位循环左移位操作
        private UInt32 ROTL(UInt32 x, int n)
        {
            return (x << n) | (x >> (32 - n));
        }

        // 布尔函数
        private UInt32 FF0(UInt32 x, UInt32 y, UInt32 z)
        {
            return x ^ y ^ z;
        }

        private UInt32 FF1(UInt32 x, UInt32 y, UInt32 z)
        {
            return (x & y) | (x & z) | (y & z);
        }

        private UInt32 GG0(UInt32 x, UInt32 y, UInt32 z)
        {
            return x ^ y ^ z;
        }

        private UInt32 GG1(UInt32 x, UInt32 y, UInt32 z)
        {
            return (x & y) | (~x & z);
        }

        // 置换函数
        private UInt32 P0(UInt32 x)
        {
            return x ^ ROTL(x, 9) ^ ROTL(x, 17);
        }

        private UInt32 P1(UInt32 x)
        {
            return x ^ ROTL(x, 15) ^ ROTL(x, 23);
        }
    }
}

Comments are closed.