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

2021SC@SDUSC

目录

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

一、引言

上一篇文章主要分析了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、真正的签名函数

山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析_第1张图片

对当前的会话环境进行配置等工作,调用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、真正的签名认证函数

山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析_第2张图片
调用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部分:数字签名算法》进行编程,实现其数字签名与验证的功能。

你可能感兴趣的