对照规范文档,来实现签名算法,前面计算消息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
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
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
2
3
4
5
6
7
8
9
10
11
12
# A6 计算s
计算
这里的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
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
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
单元测试等写完验签算法一起写
未经本人同意,禁止转载!