GMSSL :SM2椭圆曲线公钥密码算法-密钥交换协议

2022年10月13日13:15:15

2021SC@SDUSC

目录

一、整体架构

二、具体分析


前一篇文章写了密钥交换协议的基本流程,这一篇文章看一下代码实现

一、整体架构

 整体来看这一部分有6个函数,相对重要的是序号3,4代表的函数

二、具体分析

首先看第一个函数

 从名称可以初步判断出,这是对交换需要的用的数据进行初始化

分析这部分函数的代码,主要内容基本就是对结构体ctx的各个成员进行复制

ctx->id_dgstlen = sizeof(ctx->id_dgst);
ctx->remote_id_dgstlen = sizeof(ctx->remote_id_dgst);
ctx->id_dgst_md = EVP_sm3();
ctx->kdf_md = EVP_sm3();
ctx->checksum_md = EVP_sm3();
ctx->point_form = SM2_DEFAULT_POINT_CONVERSION_FORM;


ctx->is_initiator = is_initiator;
ctx->do_checksum = do_checksum;



ctx->group = EC_KEY_get0_group(ec_key);
ctx->bn_ctx = BN_CTX_new();
ctx->order = BN_new();
ctx->two_pow_w = BN_new();
ctx->t = BN_new();

再来看第二个函数↓

void SM2_KAP_CTX_cleanup(SM2_KAP_CTX *ctx)
{
	if (ctx) {
		EC_KEY_free(ctx->ec_key);
		EC_KEY_free(ctx->remote_pubkey);
		BN_CTX_free(ctx->bn_ctx);
		BN_free(ctx->two_pow_w);
		BN_free(ctx->order);
		EC_POINT_free(ctx->point);
		BN_free(ctx->t);
		memset(ctx, 0, sizeof(*ctx));
//memeset函数原型是:void* memset(void *_Dst, int  _Val, size_t _Size)_Dst是目标起始地址,_Val是要赋的值,_Size是要赋值的字节数。
	}
}

用free函数,释放空间,用memset函数给ctx的成员赋值为0

看第三个函数????

一个预备函数,判断一些参数的值,如果不符合计算要求,直接到程序最后:协商失败

do {
		if (!BN_rand_range(r, ctx->order)) {
			ECerr(EC_F_SM2_KAP_PREPARE, EC_R_RANDOM_NUMBER_GENERATION_FAILED);
			goto end;
		}

	} while (BN_is_zero(r));

//r是用随机数发生器产生的随机数,BN_is_zero(r)函数判断r是否等于0,如果等于0,则执行do之后语句,BN_rand_range(BIGNUM *rnd, BIGNUM *range);产生的0<rnd<range,如果BN_rand_range产生的r仍然等于0,产生报错,并直接结束去end部分
/*
* w = ceil(keybits / 2) - 1
 * x = 2^w + (x and (2^w - 1)) = 2^w + (x mod 2^w)
 * t = (d + x * r) mod n
 * t = (h * t) mod n
 */


//这一部分函数的主要作用就是判断在计算w,x,t者几个变量的过程中,是否会出现等于0的情况

if (!ctx->t) {
		ECerr(EC_F_SM2_KAP_PREPARE, EC_R_SM2_KAP_NOT_INITED);
		goto end;
	}

	if (!BN_nnmod(x, x, ctx->two_pow_w, ctx->bn_ctx)) {
		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
		goto end;
	}

// int BN_nnmod(BIGNUM *rem, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);r=abs(a%b)这是个取模函数

	if (!BN_add(x, x, ctx->two_pow_w)) {
		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
		goto end;
	}
//这是个加法函数 x= x + ctx->two_pow_w

	if (!BN_mod_mul(ctx->t, x, r, ctx->order, ctx->bn_ctx)) {
		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
		goto end;
	}
//ctx->t=(x*r)mod ctx->order

	if (!BN_mod_add(ctx->t, ctx->t, prikey, ctx->order, ctx->bn_ctx)) {
		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
		goto end;
	}

	if (!EC_GROUP_get_cofactor(ctx->group, h, ctx->bn_ctx)) {
		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_EC_LIB);
		goto end;
	}

	if (!BN_mul(ctx->t, ctx->t, h, ctx->bn_ctx)) {
		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
		goto end;
	}

end函数部分,主要就是free释放空间

end:
	if (h) BN_free(h);
	if (r) BN_free(r);
	if (x) BN_free(x);

	return ret;
}

看第四个函数????

这个函数是计算密钥的部分

/*
* decode point R = (x, y), encode (x, y)
* x = 2^w + (x and (2^w - 1)) = 2^w + (x mod 2^w), w = ceil(keybits / 2) - 1
* U = ht * (P + x * R)
* check U != O
*/

	if (!EC_POINT_oct2point(ctx->group, ctx->point,
		remote_point, remote_point_len, ctx->bn_ctx)) {//将remote_point中点数据转化为椭圆曲线上的点,len为数据长度;
		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
		goto end;
	}

	if (!(len = EC_POINT_point2oct(ctx->group, ctx->point, POINT_CONVERSION_UNCOMPRESSED,
		remote_pt_buf, sizeof(remote_pt_buf), ctx->bn_ctx))) {
		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
		goto end;
	}

	if (EC_METHOD_get_field_type(EC_GROUP_method_of(ctx->group)) == NID_X9_62_prime_field) //判断作用域类型是不是NID_X9_62_prime_field:X9.62的素数域;
{
		if (!EC_POINT_get_affine_coordinates_GFp(ctx->group, ctx->point, x, NULL, ctx->bn_ctx)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
			goto end;
		}
	} else {
		if (!EC_POINT_get_affine_coordinates_GF2m(ctx->group, ctx->point, x, NULL, ctx->bn_ctx)) //EC_POINT_get_affine_coordinates_GF2m,获取二进制域椭圆曲线上某个点的x和y的几何坐标,检查U点的横坐标确保它不为0
{
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
			goto end;
		}
	}

有关椭圆曲线相关函数解释可以看这篇文章OPENSSL编程 第二十章 椭圆曲线 - 测试蝈蝈 - 博客园

/* x = 2^w + (x and (2^w - 1)) = 2^w + (x mod 2^w) */

	if (!BN_nnmod(x, x, ctx->two_pow_w, ctx->bn_ctx)) {
		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_BN_LIB);
		goto end;
	}//计算(x mod 2^w)

	if (!BN_add(x, x, ctx->two_pow_w)) {
		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_BN_LIB);
		goto end;
	}
//计算2^w + (x mod 2^w)
/* U = ht * (P + x * R), check U != O */

	if (!EC_POINT_mul(ctx->group, ctx->point, NULL, ctx->point, x, ctx->bn_ctx)) {
		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
		goto end;
	}//椭圆曲线上点的乘法x * R

	if (!EC_POINT_add(ctx->group, ctx->point, ctx->point,
		EC_KEY_get0_public_key(ctx->remote_pubkey), ctx->bn_ctx)) {
		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
		goto end;
	}//椭圆曲线上点的加法(P + x * R)

	if (!EC_POINT_mul(ctx->group, ctx->point, NULL, ctx->point, ctx->t, ctx->bn_ctx)) {
		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
		goto end;
	}//椭圆曲线上点的乘法U = ht * (P + x * R)

	if (EC_POINT_is_at_infinity(ctx->group, ctx->point)) {
		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
		goto end;
	}// int EC_POINT_is_at_infinity(const EC_GROUP *group, const EC_POINT *point)
判断椭圆曲线上的点point是否是无穷远点;在这就是判断点U是不是无穷远点,如果是,则协商失败

每一步计算都判断计算的结果,看是否为0↑

/* key = KDF(xu, yu, ZA, ZB) */


	if (!ctx->kdf(share_pt_buf + 1, len - 1, key, &klen)) {
		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
		goto end;
	}

利用密钥派生函数KDF,计算key

当ctx结构体中的成员do_checksum为0时

if (ctx->do_checksum) {

		/* generate checksum S1 or SB start with 0x02
		 * S1 = SB = Hash(0x02, yu, Hash(xu, ZA, ZB, x1, y1, x2, y2))
		 */
		if (!EVP_DigestInit_ex(md_ctx, ctx->checksum_md, NULL)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}
//EVP_DigestInit_ex函数使用参数impl所指向的ENGINE设置该信息摘要结构体,参数ctx在调用本函数之前必须经过初始化。参数type一般是使用象EVP_sha1这样的函数的返回值。如果impl为NULL,那么就会使用缺省实现的信息摘要函数。大多数应用程序里面impl是设置为NULL的。操作成功返回1,否则返回0。

		bnlen = BN_num_bytes(ctx->order);

		if (!EVP_DigestUpdate(md_ctx, share_pt_buf + 1, bnlen)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}
//该函数将参数share_pt_buf + 1中的bnlen字节数据进行信息摘要到ctx结构中去,该函数可以被调用多次,用以对更多的数据进行信息摘要。操作成功返回1,否则返回0。

		if (ctx->is_initiator) {

			/* update ZA,ZB,x1,y1,x2,y2 */
			if (!EVP_DigestUpdate(md_ctx, ctx->id_dgst, ctx->id_dgstlen)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
			if (!EVP_DigestUpdate(md_ctx, ctx->remote_id_dgst, ctx->remote_id_dgstlen)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
			if (!EVP_DigestUpdate(md_ctx, ctx->pt_buf + 1, bnlen * 2)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
			if (!EVP_DigestUpdate(md_ctx, remote_pt_buf + 1, bnlen * 2)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}

		} else {

			if (!EVP_DigestUpdate(md_ctx, ctx->remote_id_dgst, ctx->remote_id_dgstlen)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
			if (!EVP_DigestUpdate(md_ctx, ctx->id_dgst, ctx->id_dgstlen)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
			if (!EVP_DigestUpdate(md_ctx, remote_pt_buf + 1, bnlen * 2)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
			if (!EVP_DigestUpdate(md_ctx, ctx->pt_buf + 1, bnlen * 2)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
		}

		if (!EVP_DigestFinal_ex(md_ctx, dgst, &dgstlen)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}

//本函数将ctx结构中的摘要信息数据返回到参数dgst中,如果参数&dgstlen不是NULL,那么摘要数据的长度(字节)就会被写入到参数&dgstlen中,大多数情况下,写入的值是EVP_MAX_MD_SIZE。在调用本函数后,不能使用相同的ctx结构调用EVP_DigestUpdate再进行数据的信息摘要操作。
//但是可以调用EVP_DigestInit_ex函数重新初始化后可以进行新的信息摘要操作。操作成功返回1,否则返回0。
		/* now dgst == H(xu,ZA,ZB,x1,y1,x2,y2)
		 */

		/* S1 = SB = Hash(0x02, yu, dgst) */

		if (!EVP_DigestInit_ex(md_ctx, ctx->checksum_md, NULL)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}

		if (!EVP_DigestUpdate(md_ctx, "\x02", 1)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}

		if (!EVP_DigestUpdate(md_ctx, share_pt_buf + 1 + bnlen, bnlen)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}

		if (!EVP_DigestUpdate(md_ctx, dgst, dgstlen)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}

		/* output S1 to local buffer or SB to output */
		if (ctx->is_initiator) {
			if (!EVP_DigestFinal_ex(md_ctx, ctx->checksum, &len)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}

		} else {
			if (!EVP_DigestFinal_ex(md_ctx, checksum, &len)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
			*checksumlen = len;
		}

		/* generate checksum SA or S2 start with 0x03
		 * SA = S2 = Hash(0x03, yu, dgst)
		 */

		if (!EVP_DigestInit_ex(md_ctx, ctx->checksum_md, NULL)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}

		if (!EVP_DigestUpdate(md_ctx, "\x03", 1)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}

		if (!EVP_DigestUpdate(md_ctx, share_pt_buf + 1 + bnlen, bnlen)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}

		if (!EVP_DigestUpdate(md_ctx, dgst, dgstlen)) {
			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
			goto end;
		}

		if (ctx->is_initiator) {
			if (!EVP_DigestFinal_ex(md_ctx, checksum, &len)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
			*checksumlen = len;

		} else {
			if (!EVP_DigestFinal_ex(md_ctx, ctx->checksum, &len)) {
				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
				goto end;
			}
		}


	}

EVP有关函数可以参考openssl之EVP系列之8---EVP_Digest系列函数详解_Secret Island-CSDN博客

第五个函数

 这个函数主要作用依旧判断

出现了一个新的函数

if (memcmp(ctx->checksum, checksum, checksumlen)) {
			ECerr(EC_F_SM2_KAP_FINAL_CHECK, EC_R_INVALID_SM2_KAP_CHECKSUM_VALUE);
			return 0;
		}
//memcmp是比较内存区域buf1和buf2的前count个字节。该函数是按字节比较的,当buf1<buf2时,返回值<0;当buf1=buf2时,返回值=0;当buf1>buf2时,返回值>0在这就是当checksum = checksumlen的时候,报错

看最后一个函数

  • 作者:vincy_yeha
  • 原文链接:https://blog.csdn.net/vincy_yeha/article/details/122160070
    更新时间:2022年10月13日13:15:15 ,共 8182 字。