/* ----------------------------------------------------------
* 文件名称: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);
}
}
}