2021SC@SDUSC|山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析

2021SC@SDUSC

目录

  • 一、引言
  • 二、密钥的生成数字签名与签名验证相关代码
    • 1、判定函数
    • 2、签名的初始化函数
    • 3、签名执行函数
    • 4、真正的签名函数
    • 5、签名验证初始化函数
    • 6、签名验证执行函数
    • 7、真正的签名认证函数
    • 8、哈希函数的判定函数
  • 三、小结

一、引言 上一篇文章主要分析了SM9的数字签名算法和相对应的签名验证算法,本篇将结合GMSSL密码库的源代码,进行进一步的相关代码分析。
二、密钥的生成数字签名与签名验证相关代码 1、判定函数 2021SC@SDUSC|山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析
文章图片

2、签名的初始化函数 2021SC@SDUSC|山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析
文章图片

int SM9_SignInit(EVP_MD_CTX *ctx, const EVP_MD *md, ENGINE *eng) { unsigned char prefix[1] = { 0x02}; (!EVP_DigestInit_ex(ctx, md, eng)) {SM9err(SM9_F_SM9_SIGNINIT, ERR_R_EVP_LIB); return 0; } if (!EVP_DigestUpdate(ctx, prefix, sizeof(prefix))) {SM9err(SM9_F_SM9_SIGNINIT, ERR_R_EVP_LIB); return 0; } return 1; }

该函数调用err.c中的一些函数,进行了错误的排出工作,初始化了签名的对话环境、所使用的模式以及引擎。
3、签名执行函数 2021SC@SDUSC|山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析
文章图片

获得主公钥Ppubs
if (ASN1_STRING_length(sk->pointPpub) != 129 || !point_from_octets(&Ppubs, ASN1_STRING_get0_data(sk->pointPpub), p, bn_ctx)) {SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_INVALID_POINTPPUB); goto end; }

计算g = e(P1, Ppubs):
if (!rate_pairing(w, &Ppubs, EC_GROUP_get0_generator(group), bn_ctx)) {SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_PAIRING_ERROR); goto end; }

在随机数r≠0时,进行循环计算w = gr、 Ha = Ha1||Ha2[0…7]、l = r - h (mod n)数字签名算法的相关运算,详细算法可见山东大学软件工程应用与实践——GMSSL开源库(三)——SM9数字签名算法及验证:
do {/* r = rand(1, n - 1) */ do {if (!BN_rand_range(r, n)) {SM9err(SM9_F_SM9_SIGNFINAL, ERR_R_BN_LIB); goto end; } } while (BN_is_zero(r)); /* w = g^r */ if (!fp12_pow(w, w, r, p, bn_ctx) || !fp12_to_bin(w, buf)) {SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_EXTENSION_FIELD_ERROR); goto end; }if (!EVP_DigestUpdate(ctx1, buf, sizeof(buf)) || !EVP_MD_CTX_copy(ctx2, ctx1) /* Ha1 = Hv(0x02||M||w||0x00000001) */ || !EVP_DigestUpdate(ctx1, ct1, sizeof(ct1)) /* Ha2 = Hv(0x02||M||w||0x00000002) */ || !EVP_DigestUpdate(ctx2, ct2, sizeof(ct2)) || !EVP_DigestFinal_ex(ctx1, buf, &len) || !EVP_DigestFinal_ex(ctx2, buf + len, &len)) {SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_DIGEST_FAILURE); goto end; }/* Ha = Ha1||Ha2[0..7] */ if (!BN_bin2bn(buf, 40, sig->h) /* h = (Ha mod (n - 1)) + 1 */ || !BN_mod(sig->h, sig->h, SM9_get0_order_minus_one(), bn_ctx) || !BN_add_word(sig->h, 1) /* l = r - h (mod n) */ || !BN_mod_sub(r, r, sig->h, n, bn_ctx)) {SM9err(SM9_F_SM9_SIGNFINAL, ERR_R_BN_LIB); goto end; } } while (BN_is_zero(r));

获取sk,及用户私钥:
if (!EC_POINT_oct2point(group, S, ASN1_STRING_get0_data(sk->privatePoint), ASN1_STRING_length(sk->privatePoint), bn_ctx)) {SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_INVALID_PRIVATE_POINT); goto end; }

随后计算S=[l]dsA:
if (!EC_POINT_mul(group, S, NULL, S, r, bn_ctx) || !(len = EC_POINT_point2oct(group, S, point_form, buf, len, bn_ctx)) || !ASN1_OCTET_STRING_set(sig->pointS, buf, len)) {SM9err(SM9_F_SM9_SIGNFINAL, ERR_R_EC_LIB); goto end; }

4、真正的签名函数 2021SC@SDUSC|山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析
文章图片

对当前的会话环境进行配置等工作,调用SM9_SignInit和SM9_SignFinal等相关函数,进行签名:
int SM9_sign(int type, /* NID_[sm3 | sha256] */ const unsigned char *data, size_t datalen, unsigned char *sig, size_t *siglen, SM9PrivateKey *sk) { int ret = 0; EVP_MD_CTX *ctx = NULL; SM9Signature *sm9sig = NULL; const EVP_MD *md; int len; if (!(md = EVP_get_digestbynid(type)) || EVP_MD_size(md) != EVP_MD_size(EVP_sm3())) {SM9err(SM9_F_SM9_SIGN, SM9_R_INVALID_HASH2_DIGEST); return 0; } if (!(ctx = EVP_MD_CTX_new())) {SM9err(SM9_F_SM9_SIGN, ERR_R_MALLOC_FAILURE); return 0; } if (!SM9_SignInit(ctx, md, NULL) || !SM9_SignUpdate(ctx, data, datalen) || !(sm9sig = SM9_SignFinal(ctx, sk))) {SM9err(SM9_F_SM9_SIGN, ERR_R_SM9_LIB); goto end; } if ((len = i2d_SM9Signature(sm9sig, &sig)) <= 0) {SM9err(SM9_F_SM9_SIGN, ERR_R_SM9_LIB); goto end; } *siglen = len; ret = 1; end: EVP_MD_CTX_free(ctx); SM9Signature_free(sm9sig); return ret; }

5、签名验证初始化函数 2021SC@SDUSC|山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析
文章图片

与上文签名初始化函数作用及代码高度类似,不再赘述。
6、签名验证执行函数 【2021SC@SDUSC|山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析】2021SC@SDUSC|山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析
文章图片

进行签名(h’,S’)的验证:
if (BN_is_zero(sig->h) || BN_cmp(sig->h, SM9_get0_order()) >= 0) {SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_INVALID_SIGNATURE); goto end; } if (!EC_POINT_oct2point(group, S, ASN1_STRING_get0_data(sig->pointS), ASN1_STRING_length(sig->pointS), bn_ctx)) {SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_INVALID_SIGNATURE); goto end; }

计算g = e(P1, Ppubs):
if (ASN1_STRING_length(pk->pointPpub) != 129 || !point_from_octets(&Ppubs, ASN1_STRING_get0_data(pk->pointPpub), p, bn_ctx)) {SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_INVALID_POINTPPUB); goto end; } if (!rate_pairing(w, &Ppubs, EC_GROUP_get0_generator(group), bn_ctx)) {SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_PAIRING_ERROR); goto end; }

进行一系列的数学算法方面的计算,我把签名验证的正确性证明粘贴到下方,有兴趣的可以同样转到上一篇文章,对数字签名的验证算法进行了解。
山东大学软件工程应用与实践——GMSSL开源库(三)——SM9数字签名算法及验证
w’ = u·t
=e(S’ ,P)gh’
=e(S’,[h1]P2 + Ppub-s) gh’
=e([r - h’]dsA , [h1]P2 + Ppub-s) gh’
=e([r - h’][ks · (h1+ks)-1]P1 , [h1]P2 + Ppub-s) gh’
=e([r - h’][ks · (h1+ks)-1]P1 , [h1][ks-1]Ppub-s + Ppub-s) gh’
=e(P1,Ppub-s)(r-h’)(ks*(h1+ks)-1)(h1* ks-1+1) · gh’
=g(r-n")(h1+ks)-1 (h1+ks) gh’
=gr
=w
/* t = g^(sig->h) */ if (!fp12_pow(w, w, sig->h, p, bn_ctx)) {SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_EXTENSION_FIELD_ERROR); goto end; } /* h1 = H1(ID||hid, N) */ if (!(md = sm9hash1_to_md(pk->hash1))) {SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_INVALID_HASH1); goto end; } if (!SM9_hash1(md, &h, (const char *)ASN1_STRING_get0_data(pk->identity), ASN1_STRING_length(pk->identity), SM9_HID_SIGN, n, bn_ctx)) {SM9err(SM9_F_SM9_VERIFYFINAL, ERR_R_SM9_LIB); goto end; } /* P = h1 * P2 + Ppubs */ if (!point_mul_generator(&P, h, p, bn_ctx) || !point_add(&P, &P, &Ppubs, p, bn_ctx) /* u = e(sig->S, P) */ || !rate_pairing(u, &P, S, bn_ctx) /* w = u * t */ || !fp12_mul(w, u, w, p, bn_ctx) || !fp12_to_bin(w, buf)) {SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_EXTENSION_FIELD_ERROR); goto end; } /* h2 = H2(M||w) mod n */ if (!EVP_DigestUpdate(ctx1, buf, sizeof(buf)) || !EVP_MD_CTX_copy(ctx2, ctx1) /* Ha1 = Hv(0x02||M||w||0x00000001) */ || !EVP_DigestUpdate(ctx1, ct1, sizeof(ct1)) /* Ha2 = Hv(0x02||M||w||0x00000002) */ || !EVP_DigestUpdate(ctx2, ct2, sizeof(ct2)) || !EVP_DigestFinal_ex(ctx1, buf, &len) || !EVP_DigestFinal_ex(ctx2, buf + len, &len)) {SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_DIGEST_FAILURE); goto end; } /* Ha = Ha1||Ha2[0..7] */ if (!BN_bin2bn(buf, 40, h) /* h2 = (Ha mod (n - 1)) + 1 */ || !BN_mod(h, h, SM9_get0_order_minus_one(), bn_ctx) || !BN_add_word(h, 1)) {SM9err(SM9_F_SM9_VERIFYFINAL, ERR_R_BN_LIB); goto end; } /* check if h2 == sig->h */ if (BN_cmp(h, sig->h) != 0) {SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_VERIFY_FAILURE); ret = 0; goto end; }

7、真正的签名认证函数 2021SC@SDUSC|山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析
文章图片

调用SM9_VerifyInit和SM9_VerifyFinal等相关函数,对来自用户A的(h,S)进行签名的认证并返回结果:
int SM9_verify(int type, /* NID_[sm3 | sha256] */ const unsigned char *data, size_t datalen, const unsigned char *sig, size_t siglen, SM9PublicParameters *mpk, const char *id, size_t idlen) { int ret = -1; EVP_MD_CTX *ctx = NULL; SM9Signature *sm9sig = NULL; SM9PublicKey *pk = NULL; const EVP_MD *md; if (!(md = EVP_get_digestbynid(type)) || EVP_MD_size(md) != EVP_MD_size(EVP_sm3())) {SM9err(SM9_F_SM9_VERIFY, SM9_R_INVALID_HASH2_DIGEST); return -1; } if (!(sm9sig = d2i_SM9Signature(NULL, &sig, siglen)) || i2d_SM9Signature(sm9sig, NULL) != siglen) {SM9err(SM9_F_SM9_VERIFY, SM9_R_INVALID_SIGNATURE_FORMAT); goto end; } if (!(pk = SM9_extract_public_key(mpk, id, idlen))) {SM9err(SM9_F_SM9_VERIFY, ERR_R_SM9_LIB); goto end; } if (!(ctx = EVP_MD_CTX_new())) {SM9err(SM9_F_SM9_VERIFY, ERR_R_MALLOC_FAILURE); goto end; } if (!SM9_VerifyInit(ctx, md, NULL) || !SM9_VerifyUpdate(ctx, data, datalen) || (ret = SM9_VerifyFinal(ctx, sm9sig, pk)) < 0) {SM9err(SM9_F_SM9_VERIFY, ERR_R_SM9_LIB); goto end; }end: EVP_MD_CTX_free(ctx); SM9Signature_free(sm9sig); SM9PublicKey_free(pk); return ret; }

8、哈希函数的判定函数 因为算法中需要用到规定的哈希函数sm3以及SHA256之一,两个函数具体内容不相同,故需要进行判定并返回值,以便后续的签名验证过程能正确进行。
static const EVP_MD *sm9hash1_to_md(const ASN1_OBJECT *hash1obj) { switch (OBJ_obj2nid(hash1obj)) { case NID_sm9hash1_with_sm3: return EVP_sm3(); case NID_sm9hash1_with_sha256: return EVP_sha256(); } return NULL; }

三、小结 这篇文章主要对GMSSL中数字签名及其验证相关代码进行了大致的结构与内容分析。针对数字签名和验证,GMSSL开发人员分别分出了init初始化函数、final相关数学计算的执行函数以及最终较为顶层的sign与verify函数,每个函数层层递进,有较强的层次性。
因为SM9算法的性质,不可避免的使用了大量的if条件语句,由if-else语句构成了算法的流程图,一步步按照国家发布的《GMT 0044.2-2016 SM9 标识密码算法 第2部分:数字签名算法》进行编程,实现其数字签名与验证的功能。

    推荐阅读