对照规范文档,来实现签名算法,前面计算消息M的摘要部分先省略,这块等后续文章实现了国密SM3算法再补充,摘要算法比较简单,比SM2算法好实现。

直接看6.1 数字签名的生成算法A3及后面部分

# A3 随机数

用随机数发生器产生随机数k ∈[1,n-1];

这里就要用到生成随机数的代码了。

gm_bn_t k;
uint8_t buf[256];
do {
    do {
        randombytes(buf, 256);
        gm_bn_from_bytes(k, buf);
    } while (gm_bn_cmp(k, GM_BN_N) >= 0);
} while (gm_bn_is_zero(k));
1
2
3
4
5
6
7
8

# A4 计算椭圆曲线点

计算椭圆曲线点(x1,y1)=[k]G

gm_point_t _P, *P = &_P;
gm_bn_t x;
gm_point_mul(P, k, GM_MONT_G);
gm_point_get_xy(P, x, NULL); // x为普通大数
1
2
3
4

这里用P暂存[k]G,签名算法只用到了x坐标,由于计算公式前面的部分都没有用到乘法,所以前面的计算部分不转换为蒙哥马利域。

# A5 计算r

计算r=(e+x1) modn,若r=0或r+k=n则返回A3

e为待签名数据的摘要,SM3摘要算法结果就是一个256-bit的大数。

gm_bn_t r;
// r = e + x (mod n)
gm_bn_add(r, e, x, GM_BN_N);

/* if r == 0 or r + k == n re-generate k */
if (gm_bn_is_zero(r)) {
    goto retry; // 返回A3
}
gm_bn_add(x, r, k, GM_BN_N);
if (gm_bn_is_zero(x)) {
    goto retry; // 返回A3
}
1
2
3
4
5
6
7
8
9
10
11
12

# A6 计算s

计算,若s=0则返回A3;

这里的d就是用于签名的私钥

/* s = ((1 + d)^-1 * (k - r * d)) mod n */
// 先将r, k转换到蒙哥马利域
gm_bn_to_mont(r, r, GM_BN_N);
gm_bn_to_mont(k, k, GM_BN_N);

// e = r * d
gm_bn_mont_mul(e, r, d, GM_BN_N);
// k = k - r * d
gm_bn_sub(k, k, e, GM_BN_N);

// x = 1 + d
gm_bn_add(x, GM_BN_MONT_NONE, d, GM_BN_N);
// x = (1 + d)^-1
gm_bn_inv(x, x, GM_BN_N);
// s = ((1 + d)^-1 * (k - r * d)) mod n
gm_bn_mont_mul(s, x, k, GM_BN_N);

if(gm_bn_is_zero(s)) {
    goto retry;
}

// 将s转换回普通大数
gm_bn_from_mont(s, s, GM_BN_N);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 完整代码

这里的testK,是用来方便编写单元测试用的,因为如果用随机数k的话,签名预期结果不好判断。

int gm_do_sign(const gm_bn_t key, const gm_bn_t dgst, unsigned char *sig) {
    return gm_do_sign_for_test(key, dgst, sig, NULL);
}

int gm_do_sign_for_test(const gm_bn_t key, const gm_bn_t dgst, unsigned char *sig, const gm_bn_t testK) {
    gm_point_t _P, *P = &_P;
    gm_bn_t d;
    gm_bn_t e;
    gm_bn_t k;
    gm_bn_t x;
    gm_bn_t r;
    gm_bn_t s;

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

    gm_bn_to_mont(d, key, GM_BN_N);

    // e = H(M)
    gm_bn_copy(e, dgst);

retry:
    if(NULL == testK) {
        // rand k in [1, n - 1]
        uint8_t buf[256];
        do {
            do {
                randombytes(buf, 256);
                gm_bn_from_bytes(k, buf);
            } while (gm_bn_cmp(k, GM_BN_N) >= 0);
        } while (gm_bn_is_zero(k));
    } else {
        gm_bn_copy(k, testK);
    }

    // (x, y) = kG
    gm_point_mul(P, k, GM_MONT_G);
    gm_point_get_xy(P, x, NULL);


    // r = e + x (mod n)
    gm_bn_add(r, e, x, GM_BN_N);

    /* if r == 0 or r + k == n re-generate k */
    if (gm_bn_is_zero(r)) {
        goto retry;
    }
    gm_bn_add(x, r, k, GM_BN_N);
    if (gm_bn_is_zero(x)) {
        goto retry;
    }

    gm_bn_to_bytes(r, sig);

    /* s = ((1 + d)^-1 * (k - r * d)) mod n */
    gm_bn_to_mont(r, r, GM_BN_N);
    gm_bn_to_mont(k, k, GM_BN_N);

    gm_bn_mont_mul(e, r, d, GM_BN_N);
    gm_bn_sub(k, k, e, GM_BN_N);

    gm_bn_add(x, GM_BN_MONT_NONE, d, GM_BN_N);
    gm_bn_inv(x, x, GM_BN_N);
    gm_bn_mont_mul(s, x, k, GM_BN_N);

    if(gm_bn_is_zero(s)) {
        goto retry;
    }

    gm_bn_from_mont(s, s, GM_BN_N);
    gm_bn_to_bytes(s, sig + 32);
    return 1;
}
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

单元测试等写完验签算法一起写