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的时候,报错
看最后一个函数