代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> #define ROTL(x, n) (((x) << (n)) | ((x) > > (32 - (n)))) #define P0(x) ((x) ^ ROTL((x), 9) ^ ROTL((x), 17)) #define P1(x) ((x) ^ ROTL((x), 15) ^ ROTL((x), 23)) static const uint32_t IV[8 ] = { 0x7380166fU L, 0x4914b2b9U L, 0x172442d7U L, 0xda8a0600U L, 0xa96f30bcU L, 0x163138aaU L, 0xe38dee4dU L, 0xb0fb0e4eU L}; static const uint32_t Tj_base0 = 0x79cc4519U L;static const uint32_t Tj_base1 = 0x7a879d8aU L;static inline uint32_t FFj (uint32_t x, uint32_t y, uint32_t z, int j) { if (j >= 0 && j <= 15 ) return x ^ y ^ z; return (x & y) | (x & z) | (y & z); } static inline uint32_t GGj (uint32_t x, uint32_t y, uint32_t z, int j) { if (j >= 0 && j <= 15 ) return x ^ y ^ z; return (x & y) | ((~x) & z); } static uint32_t GETU32_BE (const uint8_t *b) { return ((uint32_t )b[0 ] << 24 ) | ((uint32_t )b[1 ] << 16 ) | ((uint32_t )b[2 ] << 8 ) | (uint32_t )b[3 ]; } static void PUTU32_BE (uint8_t *b, uint32_t v) { b[0 ] = (uint8_t )(v >> 24 ); b[1 ] = (uint8_t )(v >> 16 ); b[2 ] = (uint8_t )(v >> 8 ); b[3 ] = (uint8_t )(v); } void sm3_compress (uint32_t V[8 ], const uint8_t block[64 ]) { uint32_t W[68 ]; uint32_t W1[64 ]; for (int j = 0 ; j < 16 ; ++j) { W[j] = GETU32_BE(block + j * 4 ); } for (int j = 16 ; j < 68 ; ++j) { uint32_t x = W[j - 16 ] ^ W[j - 9 ] ^ ROTL(W[j - 3 ], 15 ); W[j] = P1(x) ^ ROTL(W[j - 13 ], 7 ) ^ W[j - 6 ]; } for (int j = 0 ; j < 64 ; ++j) { W1[j] = W[j] ^ W[j + 4 ]; } uint32_t A = V[0 ], B = V[1 ], C = V[2 ], D = V[3 ]; uint32_t E = V[4 ], F = V[5 ], G = V[6 ], H = V[7 ]; for (int j = 0 ; j < 64 ; ++j) { uint32_t Tj = (j <= 15 ) ? Tj_base0 : Tj_base1; uint32_t T_rot = ROTL(Tj, j & 31 ); uint32_t SS1 = ROTL((ROTL(A, 12 ) + E + T_rot), 7 ); uint32_t SS2 = SS1 ^ ROTL(A, 12 ); uint32_t TT1 = (FFj(A, B, C, j) + D + SS2 + W1[j]); uint32_t TT2 = (GGj(E, F, G, j) + 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; } void sm3_hash (const uint8_t *msg, size_t msglen, uint8_t out[32 ]) { uint64_t bitlen = (uint64_t )msglen * 8ULL ; size_t k = (56 - (msglen + 1 ) % 64 ) % 64 ; size_t padlen = 1 + k + 8 ; size_t total = msglen + padlen; uint8_t *buf = (uint8_t *)malloc (total); if (!buf) { fprintf (stderr , "内存分配失败\n" ); return ; } memcpy (buf, msg, msglen); buf[msglen] = 0x80 ; if (k) memset (buf + msglen + 1 , 0 , k); for (int i = 0 ; i < 8 ; ++i) { buf[msglen + 1 + k + i] = (uint8_t )(bitlen >> (56 - 8 * i)); } uint32_t V[8 ]; for (int i = 0 ; i < 8 ; ++i) V[i] = IV[i]; size_t blocks = total / 64 ; for (size_t i = 0 ; i < blocks; ++i) { sm3_compress(V, buf + i * 64 ); } free (buf); for (int i = 0 ; i < 8 ; ++i) { PUTU32_BE(out + i * 4 , V[i]); } } void print_hex (const uint8_t *buf, size_t len) { for (size_t i = 0 ; i < len; ++i) { printf ("%02x" , buf[i]); } printf ("\n" ); } int main (void ) { char input[1024 ]; uint8_t digest[32 ]; printf ("请输入要进行 SM3 的字符串(可为空):\n" ); if (!fgets(input, sizeof (input), stdin )) { fprintf (stderr , "读取输入失败\n" ); return 1 ; } size_t len = strlen (input); if (len > 0 && input[len - 1 ] == '\n' ) input[len - 1 ] = '\0' ; sm3_hash((const uint8_t *)input, strlen (input), digest); printf ("sm3(\"%s\") = " , input); print_hex(digest, 32 ); return 0 ; }
代码解释 消息填充模块 1 sm3_hash(const uint8_t *msg, size_t msglen, uint8_t out[32 ])
实际上相当于函数入口 msg代表输入信息msglen代表消息长度out代表将要输出的256位的sm3杂凑值
1 uint64_t bitlen = (uint64_t )msglen * 8ULL ;
bitlen用于存储原文长度,uint64_t代表类型为64位无符号整数,因为SM3 标准规定:消息长度使用 64 位存储,不能多也不能少(uint64_t)msglen用于将msglen显式转换为uint64_t类型msglen代表输入消息的字节长度
1 size_t k = (56 - (msglen + 1 ) % 64 ) % 64 ;
k表示需要填充的0的数量,前面说过需要填充0使得l+1+k≡448(mod 512)
1 2 size_t padlen = 1 + k + 8 ;size_t total = msglen + padlen;
padlen代表了总共需要在原来输入中添加多少字节(1字节=8位)total即为输入长度+填充长度
1 2 3 4 5 6 7 8 uint8_t *buf = (uint8_t *)malloc (total);memcpy (buf, msg, msglen);buf[msglen] = 0x80 ; memset (buf + msglen + 1 , 0 , k);for (int i = 0 ; i < 8 ; ++i){ buf[msglen + 1 + k + i] = (uint8_t )(bitlen >> (56 - 8 * i)); }
buf填充的具体步骤如下:
给buf分配总共需要的大小,用于存储填充后的完整消息
将原始信息msg输入到buf中
在buf中填充1(还要附加7个0组成一字节,但不可能越界,因为一个字符占8位)
将需要填充的0填入buf
将64位的长度信息填入buf,这里buf[msglen + 1 + k + i]是为了跳过原始信息长度msglen,1位填充位,k个填充0,最后的+i是每个长度信息的偏移量
消息扩展模块 1 2 3 4 5 size_t blocks = total / 64 ;for (size_t i = 0 ; i < blocks; ++i){ sm3_compress(V, buf + i * 64 ); }
将填充完毕后的信息分组,512位为1组,再让每组开始进行消息扩展
1 void sm3_compress (uint32_t V[8 ], const uint8_t block[64 ])
消息扩展函数V用于等会的压缩函数作为初始数值block即表示当前组,每组都要进行一次扩展和压缩
接下来要将消息扩展为132个32位的W(W0, W1, …, W67 和 W‘0, W’1, …, W‘63)
1 2 3 4 for (int j = 0 ; j < 16 ; ++j){ W[j] = GETU32_BE(block + j * 4 ); }
前16个W很简单,就是将分组中的内容平分位16块填入W0-W15
1 2 3 4 5 for (int j = 16 ; j < 68 ; ++j){ uint32_t x = W[j - 16 ] ^ W[j - 9 ] ^ ROTL(W[j - 3 ], 15 ); W[j] = P1(x) ^ ROTL(W[j - 13 ], 7 ) ^ W[j - 6 ]; }
该段为W16到W67用此方法扩展,其中ROTL具体为
1 #define ROTL(x, n) (((x) << (n)) | ((x) > > (32 - (n))))
其中P1具体为
1 #define P1(x) ((x) ^ ROTL((x), 15) ^ ROTL((x), 23))
1 2 3 4 for (int j = 0 ; j < 64 ; ++j){ W1[j] = W[j] ^ W[j + 4 ]; }
该段为W’0 到 W’63的扩展方法,也是W比W’多4位的原因 至此消息扩展完成
压缩函数 初始化寄存器
1 2 uint32_t A = V[0 ], B = V[1 ], C = V[2 ], D = V[3 ];uint32_t E = V[4 ], F = V[5 ], G = V[6 ], H = V[7 ];
接下来的每轮压缩都会得到一个新的ABCDEFGH的值,直到最后一组消息分组进行完压缩,最后的ABCDEFGH便是消息的杂凑值
64轮迭代函数如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 for (int j = 0 ; j < 64 ; ++j){ uint32_t Tj = (j <= 15 ) ? Tj_base0 : Tj_base1; uint32_t T_rot = ROTL(Tj, j & 31 ); uint32_t SS1 = ROTL((ROTL(A, 12 ) + E + T_rot), 7 ); uint32_t SS2 = SS1 ^ ROTL(A, 12 ); uint32_t TT1 = (FFj(A, B, C, j) + D + SS2 + W1[j]); uint32_t TT2 = (GGj(E, F, G, j) + 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); }
其中,SS1,SS2,TT1,TT2都只是一个暂存的容器, Tj是一个随着循环轮数变换的常数 具体为Tj = 79cc4519 (前16轮) 或 7a879d8a (后48轮) ROTL在前文有做过解释 FFj和GGj是布尔函数,具体实现如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static inline uint32_t FFj (uint32_t x, uint32_t y, uint32_t z, int j) { if (j >= 0 && j <= 15 ) return x ^ y ^ z; return (x & y) | (x & z) | (y & z); } static inline uint32_t GGj (uint32_t x, uint32_t y, uint32_t z, int j) { if (j >= 0 && j <= 15 ) return x ^ y ^ z; return (x & y) | ((~x) & z); }
最后,当各个分组做完64轮迭代后,我们便可以得到新的ABCDEFGH,每个变量占32位共256位,这便是最终的杂凑值
整体接口 1 2 3 4 5 6 7 8 9 10 int main (void ) { char input[1024 ]; uint8_t digest[32 ]; printf ("请输入要进行 SM3 的字符串(可为空):\n" ); sm3_hash((const uint8_t *)input, strlen (input), digest); printf ("sm3(\"%s\") = " , input); print_hex(digest, 32 ); return 0 ; }
在整体接口中,系统会提示用户 “请输入要进行 SM3 的字符串(可为空)” ,用户需要输入最多1023个字符(也可以不输入直接计算空字符的sm3杂凑值)
接着系统调用sm3_hash来开始计算sm3杂凑值,并将结果存储到256位的结果变量digest 最后将输出杂凑值,此接口简单易用