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

首页 / 新闻资讯 / 正文

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的时候,报错

看最后一个函数