山东大学软件工程应用与实践——GMSSL开源库——SM9数字签名算法及验证的源代码分析
Posted 乔未
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了山东大学软件工程应用与实践——GMSSL开源库——SM9数字签名算法及验证的源代码分析相关的知识,希望对你有一定的参考价值。
2021SC@SDUSC
目录
一、引言
上一篇文章主要分析了SM9的数字签名算法和相对应的签名验证算法,本篇将结合GMSSL密码库的源代码,进行进一步的相关代码分析。
二、密钥的生成数字签名与签名验证相关代码
1、判定函数
2、签名的初始化函数
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、签名执行函数
获得主公钥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、真正的签名函数
对当前的会话环境进行配置等工作,调用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、签名验证初始化函数
与上文签名初始化函数作用及代码高度类似,不再赘述。
6、签名验证执行函数
进行签名(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、真正的签名认证函数
调用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部分:数字签名算法》进行编程,实现其数字签名与验证的功能。
以上是关于山东大学软件工程应用与实践——GMSSL开源库——SM9数字签名算法及验证的源代码分析的主要内容,如果未能解决你的问题,请参考以下文章