# 头文件定义

typedef struct {
    unsigned int state[8]; // 寄存器中间状态
    unsigned char buf[64]; // 待压缩消息
    unsigned int cur_buf_len; // 当前待压缩消息长度(字节)
    uint64_t compressed_len; // 已压缩消息长度(比特)
}gm_sm3_context;

/**
 * 摘要算法初始化
 * @param ctx 上下文
 */
void gm_sm3_init(gm_sm3_context * ctx);

/**
 * 添加消息
 * @param ctx 上下文
 * @param input 消息
 * @param iLen 消息长度(字节)
 */
void gm_sm3_update(gm_sm3_context * ctx, const unsigned char * input, unsigned int iLen);

/**
 * 计算摘要
 * @param ctx 上下文
 * @param output 输出摘要结果
 */
void gm_sm3_done(gm_sm3_context * ctx, unsigned char * output);

/**
 * 直接计算消息的摘要
 * @param input 消息
 * @param iLen 消息长度(字节)
 * @param output 输出摘要结果
 */
void gm_sm3(const unsigned char * input, unsigned int iLen, unsigned char * output);
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

# gm_sm3_init

初始化函数,就是将寄存器的值初始化一下,长度这些初始化一下

void gm_sm3_init(gm_sm3_context * ctx) {
    ctx->state[0] = GM_SM3_IV_A;
    ctx->state[1] = GM_SM3_IV_B;
    ctx->state[2] = GM_SM3_IV_C;
    ctx->state[3] = GM_SM3_IV_D;
    ctx->state[4] = GM_SM3_IV_E;
    ctx->state[5] = GM_SM3_IV_F;
    ctx->state[6] = GM_SM3_IV_G;
    ctx->state[7] = GM_SM3_IV_H;
    ctx->cur_buf_len = 0;
    ctx->compressed_len = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12

# gm_sm3_update

往摘要算法上下文中添加待计算的消息,每当消息长度满64字节(一个BlockSize)时,调用一次压缩算法进行压缩

void gm_sm3_update(gm_sm3_context * ctx, const unsigned char * input, unsigned int iLen) {
    while (iLen--)
    {
        ctx->buf[ctx->cur_buf_len] = *input++;
        ctx->cur_buf_len++;

        /* 是否满64个字节 */
        if (ctx->cur_buf_len == 64)
        {
            // 满了,则立即调用压缩函数进行压缩
            gm_sm3_compress(ctx);
            ctx->compressed_len += 512;
            ctx->cur_buf_len = 0;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# gm_sm3_done

计算最终摘要值,计算前要把消息最终的长度及填充计算出来,然后再调用压缩算法计算最终结果

#ifndef GM_PUT_UINT32_BE
#define GM_PUT_UINT32_BE(n, b ,i)                            \
{                                                       \
    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
    (b)[(i) + 3] = (unsigned char) ( (n)       );       \
}
#endif

static const unsigned char gm_sm3_padding[64] = {
        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
};

void gm_sm3_done(gm_sm3_context * ctx, unsigned char * output) {
    uint32_t padn;
    unsigned char msglen[8];
    uint64_t total_len, high, low;

    // 消息的总长度(比特) = 剩余未压缩数据的长度(字节) * 8
    total_len = ctx->compressed_len + (ctx->cur_buf_len << 3);
    high = (total_len >> 32) & 0x0FFFFFFFF;
    low = total_len & 0x0FFFFFFFF;

    GM_PUT_UINT32_BE(high, msglen, 0);
    GM_PUT_UINT32_BE(low,  msglen, 4);

    // 计算填充长度,因为事先要添加一比特,故应计算cur_buf_len + 1是否超过56
    padn = ((ctx->cur_buf_len + 1) < 56) ? (56 - ctx->cur_buf_len) : (120 - ctx->cur_buf_len);

    // 添加填充
    gm_sm3_update(ctx, (unsigned char *) gm_sm3_padding, padn);
    gm_sm3_update(ctx, msglen, 8);

    GM_PUT_UINT32_BE(ctx->state[0], output,  0);
    GM_PUT_UINT32_BE(ctx->state[1], output,  4);
    GM_PUT_UINT32_BE(ctx->state[2], output,  8);
    GM_PUT_UINT32_BE(ctx->state[3], output, 12);
    GM_PUT_UINT32_BE(ctx->state[4], output, 16);
    GM_PUT_UINT32_BE(ctx->state[5], output, 20);
    GM_PUT_UINT32_BE(ctx->state[6], output, 24);
    GM_PUT_UINT32_BE(ctx->state[7], output, 28);
}
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

# gm_sm3

void gm_sm3(const unsigned char * input, unsigned int iLen, unsigned char * output) {
    gm_sm3_context ctx;
    gm_sm3_init(&ctx);
    gm_sm3_update(&ctx, input, iLen);
    gm_sm3_done(&ctx, output);
}
1
2
3
4
5
6

# 单元测试

void test_gm_sm3(const unsigned char * input, unsigned int iLen, const unsigned char * output_hex) {
    gm_sm3_context ctx;
    int i = 0;
    unsigned char buf[32] = {0};
    char res[65] = {0};

    gm_sm3(input, iLen, buf);

    gm_sm3_init(&ctx);
    gm_sm3_update(&ctx, input, iLen);

    for(i = 0; i < 100000; i++) { // 10万次
        gm_sm3(buf, 31, buf);
        gm_sm3_update(&ctx, buf, i % 32);
    }

    gm_sm3_done(&ctx, buf);

    for (i = 0; i < 32; i++) {
        sprintf(res + i * 2, "%02X", (buf[i] & 0x0FF));
    }

    printf("r = %s\n", res);
    printf("test result: %s\n", (strcmp(output_hex, res) == 0 ? "ok" : "fail"));
}
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

main函数增加:

if(strcmp(argv[1], "gm_sm3") == 0) {
    test_gm_sm3("abc", 3, "DC7E07FF06247D00B4A8D1837C8F8B2A26C3C67C2EEE81B1E7CF9400B51891CB");
}
1
2
3

算法效率:

saintdeMacBook-Pro:bn saint$ time ./a.out gm_sm3
r = DC7E07FF06247D00B4A8D1837C8F8B2A26C3C67C2EEE81B1E7CF9400B51891CB
test result: ok

real	0m0.320s
user	0m0.312s
sys	0m0.005s
1
2
3
4
5
6
7