对照规范文档7.1 数字签名的验证算法,先略过B3、B4,这块等后续文章实现了国密SM3算法再补充

# B1-2 检验r及s

B1:检验r′ ∈[1,n-1]是否成立,若不成立则验证不通过; B2:检验s′ ∈[1,n-1]是否成立,若不成立则验证不通过;

if (gm_bn_is_zero(r)
    || gm_bn_cmp(r, GM_BN_N) >= 0
    || gm_bn_is_zero(s)
    || gm_bn_cmp(s, GM_BN_N) >= 0) {
    return -1;
}
1
2
3
4
5
6

# B5 计算t

计算t = (r′ + s′) modn, 若t = 0,则验证不通过

// t = r + s (mod n)
// check t != 0
gm_bn_add(t, r, s, GM_BN_N);
if (gm_bn_is_zero(t)) {
    return -1;
}
1
2
3
4
5
6

# B6 计算椭圆曲线点

计算椭圆曲线点

这里PA即用于验签的公钥,代码中用点P表示,算法中只用到了坐标x,所以只取x的值

// Q = s * G + t * P
gm_point_mul(Q, s, GM_MONT_G);
gm_point_mul(P, t, P);
gm_point_add(Q, Q, P);
gm_point_get_xy(Q, x, NULL);
1
2
3
4
5

# B7 计算R

计算,检 验R=r′是否成立,若成立则验证通过;否则验证不通过

// e  = H(M)
// r' = e + x (mod n)
gm_bn_copy(e, dgst);
gm_bn_add(e, e, x, GM_BN_N);

// check if r == r'
if (gm_bn_cmp(e, r) == 0) {
    return 1;
} else {
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11

# 完整代码

int gm_do_verify(const gm_point_t *key, const gm_bn_t dgst, const unsigned char *sig) {
    gm_point_t _P, *P = &_P;
    gm_point_t _Q, *Q = &_Q;
    gm_bn_t r;
    gm_bn_t s;
    gm_bn_t e;
    gm_bn_t x;
    gm_bn_t t;

    if (!key || !dgst || !sig) {
        return -1;
    }

    // parse signature values
    gm_bn_from_bytes(r, (const uint8_t *)sig);
    gm_bn_from_bytes(s, (const uint8_t *)sig + 32);
    if (gm_bn_is_zero(r)
        || gm_bn_cmp(r, GM_BN_N) >= 0
        || gm_bn_is_zero(s)
        || gm_bn_cmp(s, GM_BN_N) >= 0) {
        return -1;
    }

    // parse public key
    gm_point_copy(P, key);

    // t = r + s (mod n)
    // check t != 0
    gm_bn_add(t, r, s, GM_BN_N);
    if (gm_bn_is_zero(t)) {
        return -1;
    }

    // Q = s * G + t * P
    gm_point_mul(Q, s, GM_MONT_G);
    gm_point_mul(P, t, P);
    gm_point_add(Q, Q, P);
    gm_point_get_xy(Q, x, NULL);

    // e  = H(M)
    // r' = e + x (mod n)
    gm_bn_copy(e, dgst);
    gm_bn_add(e, e, x, GM_BN_N);

    // check if r == r'
    if (gm_bn_cmp(e, r) == 0) {
        return 1;
    } else {
        return 0;
    }
}
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

# 单元测试

/**
 * 国密SM2单元测试
 * @param key_hex 私钥十六进制 
 * @param pubKey_hex 公钥十六进制
 * @param sig_hex 预期结果十六进制
 * @param algType 算法,0签名及验签,1仅签名,2仅验签
 */
void test_gm_sv(const char * key_hex, const char * pubKey_hex, const char * sig_hex, int algType) {
    unsigned char sig_res[256] = {0};
    gm_bn_t testK;
    gm_bn_t key;
    gm_bn_t dgst;
    gm_point_t _P, *P = &_P;
    int i, j;

    gm_bn_from_hex(testK, key_hex);
    gm_bn_from_hex(dgst, key_hex);
    gm_bn_from_hex(key, key_hex);
    gm_point_from_hex(P, pubKey_hex);

    if(algType == 0) { // sign and verify
        for (i = 0; i < 1000; i++) {
            if (gm_do_sign_for_test(key, dgst, sig_res + 64, testK) != 1) {
                printf("gm do sign failed.\n");
            }
            if (gm_do_verify(P, dgst, sig_res + 64) != 1) {
                printf("gm do verify failed.\n");
            }
            for (j = 0; j < 32; j++) {
                sig_res[j] = sig_res[64 + j] ^ sig_res[96 + j];
            }
            gm_bn_from_bytes(dgst, sig_res);
        }
    }else if(algType == 1) { // sign only
        for (i = 0; i < 1000; i++) {
            if (gm_do_sign_for_test(key, dgst, sig_res + 64, testK) != 1) {
                printf("gm do sign failed.\n");
            }
            for (j = 0; j < 32; j++) {
                sig_res[j] = sig_res[64 + j] ^ sig_res[96 + j];
            }
            gm_bn_from_bytes(dgst, sig_res);
        }
    }else if(algType == 2) { // verify only
        gm_bn_from_hex(key, sig_hex);
        gm_bn_to_bytes(key, sig_res + 64);
        gm_bn_from_hex(key, sig_hex + 64);
        gm_bn_to_bytes(key, sig_res + 96);

        for (i = 0; i < 1000; i++) {
            if (gm_do_verify(P, dgst, sig_res + 64) != 1) {
                printf("gm do verify failed.\n");
            }
        }
    }

    int k;
    for (k = 0; k < 64; k++) {
        sprintf(sig_res + k * 2, "%02X", (sig_res[64 + k] & 0x0FF));
    }

    sig_res[129] = 0;

    printf("r = %s\n", sig_res);
    printf("test result: %s\n", (strcmp(sig_hex, sig_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
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

main函数增加:

if(strcmp(argv[1], "gm_sv") == 0) {
    test_gm_sv("6B8B4567327B23C6643C98696633487374B0DC5119495CFF2AE8944A625558EC",
                   "0148E6AF89A0E132E4E7CDA26DF2C2AEB53B741FD00AE85C78CF6EBA13E939B12F58B1E8A661EBF3395459F28945D381259BEEDA76B4886FABF5EE0A55ADEEB2",
                   "D6125763F2825F35494E930245D064E408553678A200D018E6217975E19EEFE68E48E00F0BF9632826F64F84122627A36F0F998CDB120327F4BC7ABF84E86FE4",
                   0);
}

if(strcmp(argv[1], "gm_sign") == 0) {
    test_gm_sv("6B8B4567327B23C6643C98696633487374B0DC5119495CFF2AE8944A625558EC",
               "0148E6AF89A0E132E4E7CDA26DF2C2AEB53B741FD00AE85C78CF6EBA13E939B12F58B1E8A661EBF3395459F28945D381259BEEDA76B4886FABF5EE0A55ADEEB2",
               "D6125763F2825F35494E930245D064E408553678A200D018E6217975E19EEFE68E48E00F0BF9632826F64F84122627A36F0F998CDB120327F4BC7ABF84E86FE4",
               1);
}

if(strcmp(argv[1], "gm_verify") == 0) {
    test_gm_sv("6B8B4567327B23C6643C98696633487374B0DC5119495CFF2AE8944A625558EC",
               "0148E6AF89A0E132E4E7CDA26DF2C2AEB53B741FD00AE85C78CF6EBA13E939B12F58B1E8A661EBF3395459F28945D381259BEEDA76B4886FABF5EE0A55ADEEB2",
               "6CD42C16BC1C04F94924660BD4260B2229EC5070E954455BA3B80304763E929DB86361E8EAF0497DA53DD2DDE83F6CEA3C2C4838E9D3E29BDB269D9C5BF52976",
               2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

算法效率:

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

real	0m19.420s
user	0m18.444s
sys	0m0.205s
saintdeMacBook-Pro:bn saint$ time ./a.out gm_sign
r = D6125763F2825F35494E930245D064E408553678A200D018E6217975E19EEFE68E48E00F0BF9632826F64F84122627A36F0F998CDB120327F4BC7ABF84E86FE4
test result: ok

real	0m6.728s
user	0m6.574s
sys	0m0.047s
saintdeMacBook-Pro:bn saint$ time ./a.out gm_verify
r = 6CD42C16BC1C04F94924660BD4260B2229EC5070E954455BA3B80304763E929DB86361E8EAF0497DA53DD2DDE83F6CEA3C2C4838E9D3E29BDB269D9C5BF52976
test result: ok

real	0m12.129s
user	0m11.840s
sys	0m0.082s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

签名的效率大约是7ms一次,用在APP客户端完全可以满足性能要求,用户无感知,另外代码无任何依赖,没有大的预计算表,可执行文件大概31KB。