使用OpenSSL验证EC公钥的签名,在释放ECKey时导致崩溃。

huangapple go评论60阅读模式
英文:

validating signature using EC public key in openssl causing crash while freeing ECKey

问题

以下是您提供的代码部分的中文翻译:

// 我有一个 EC 公钥坐标,分别为 "x" 和 "y",它们是 64 字节的字符串。
// 我有一条消息,必须使用我拥有的签名进行验证。
// 我编写了下面显示的函数。在释放密钥时,该函数崩溃,即在使用行 EC_KEY_free(ec_key) 时。
// 如果我注释掉这一行,就不会出现崩溃。请提供您的意见/提示,为什么这一行会导致问题。感谢您的指导。

// 例如,公钥 "x" 和 "y" 的值如下:

// x 为 5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE
// y 为 C05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1

// 在使用上述值后,我拥有的 BIGNUM 打印值如下:

// BIGNUM x 坐标:0x5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE
// BIGNUM y 坐标:0xC05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1

int verifySignature(const unsigned char* message, size_t message_len, const unsigned char* signature, size_t signature_len, const char* public_key_x, const char* public_key_y) {
    int result = 0;
    EC_KEY* ec_key = NULL;
    EC_POINT* ec_point = NULL;
    EVP_PKEY* public_key = NULL;
    EVP_MD_CTX* md_ctx = NULL;

    // 解析公钥
    BIGNUM* x = BN_new();
    BIGNUM* y = BN_new();

    if (!BN_hex2bn(&x, public_key_x)) {
        perror("解析公钥 x 坐标时出错");
        return -1;
    }

    printBN("BIGNUM x 坐标:", x);

    if (!BN_hex2bn(&y, public_key_y)) {
        perror("解析公钥 y 坐标时出错");
        return -1;
    }

    printBN("BIGNUM y 坐标:", y);

    /*
    EC_KEY 代表公钥和(可选的)相关私钥。
    可以通过调用 EC_KEY_new 来构建一个新的 EC_KEY(没有关联的曲线)。
    新创建的 EC_KEY 的引用计数最初设置为 1。
    可以通过调用 EC_KEY_set_group 来将曲线与 EC_KEY 关联起来。

    或者,可以通过调用 EC_KEY_new_by_curve_name 来构建一个新的 EC_KEY,
    并提供关联曲线的 nid(命名标识符)。
    运行 "openssl ecparam -list_curves" 查看曲线名称。
    该函数简单地包装了对 EC_KEY_new 和 EC_GROUP_new_by_curve_name 的调用。
    */
    // 使用曲线 SECP256R1 创建 EC 密钥。
    if ((ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) {
        fprintf(stderr, "创建 EC 密钥时出错\n");
        goto end;
    }

    // 创建 EC 点。EC_KEY_get0_group 返回与 EC_KEY 关联的 EC_GROUP。
    if ((ec_point = EC_POINT_new(EC_KEY_get0_group(ec_key))) == NULL) {
        fprintf(stderr, "创建 EC 点时出错\n");
        goto end;
    }

    // 设置点的 x 和 y 坐标
    if (!EC_POINT_set_affine_coordinates_GFp(EC_KEY_get0_group(ec_key), ec_point, x, y, NULL)) {
        fprintf(stderr, "设置 EC 点坐标时出错\n");
        goto end;
    }

    // 设置 EC 密钥的公钥
    if (!EC_KEY_set_public_key(ec_key, ec_point)) {
        fprintf(stderr, "设置 EC 公钥时出错\n");
        goto end;
    }

    // 将 EC 密钥分配给 EVP_PKEY
    if ((public_key = EVP_PKEY_new()) == NULL) {
        fprintf(stderr, "创建 EVP_PKEY 时出错\n");
        goto end;
    }

    if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
        fprintf(stderr, "将 EC 密钥分配给 EVP_PKEY 时出错\n");
        goto end;
    }

    // 创建消息摘要上下文
    if ((md_ctx = EVP_MD_CTX_new()) == NULL) {
        fprintf(stderr, "创建消息摘要上下文时出错\n");
        goto end;
    }

    // 使用 SHA-256 和公钥初始化消息摘要上下文
    if (!EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, public_key)) {
        fprintf(stderr, "初始化消息摘要上下文时出错\n");
        goto end;
    }

    // 使用消息更新消息摘要上下文
    if (!EVP_DigestVerifyUpdate(md_ctx, message, message_len)) {
        fprintf(stderr, "更新消息摘要上下文时出错\n");
        goto end;
    }

    // 验证签名
    result = EVP_DigestVerifyFinal(md_ctx, signature, signature_len);
    printf("结果:%d\n", result);

end:
    if (result != 1) {
        ERR_print_errors_fp(stderr);
    }

    if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx);
    if (public_key != NULL) EVP_PKEY_free(public_key);
    if (ec_point != NULL) EC_POINT_free(ec_point);
    
    // 如果 (ec_key != NULL) 则释放 EC_KEY(FIXME: 这里导致崩溃)
    if (ec_key != NULL) EC_KEY_free(ec_key);

    return result;
}

请注意,我只翻译了您提供的代码部分,没有包含其他内容。如果您需要进一步的帮助或解释,请随时提问。

英文:

I have EC Public key coordinates "x" and "y" which are 64 bytes string. I have a message which has to be validated with signature I have. I have written a function as shown below. Below function is crashing while freeing the key using line EC_KEY_free(ec_key). If I comment this line no crash is observed. Require your inputs/hints why this line is causing a problem. Thanks for your guidance.

For example public key "x" and "y" values are

x is 5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE 
y is C05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1 

After using BIGNUM with above values. I have BN print values as shown below

BIGNUM x coordinate:  0x5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE
BIGNUM y coordinate:  0xC05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1
int verifySignature(const unsigned char* message, size_t message_len, const unsigned char* signature, size_t signature_len, const char* public_key_x, const char* public_key_y) {
int result = 0;
EC_KEY* ec_key = NULL;
EC_POINT* ec_point = NULL;
EVP_PKEY* public_key = NULL;
EVP_MD_CTX* md_ctx = NULL;
// parse public key
BIGNUM* x = BN_new();
BIGNUM* y = BN_new();
if (!BN_hex2bn(&x, public_key_x)) {
perror("Error parsing public key x coordinate");
return -1;
}
printBN("BIGNUM x coordinate: ", x);
if (!BN_hex2bn(&y, public_key_y)) {
perror("Error parsing public key y coordinate");
return -1;
}
printBN("BIGNUM y coordinate: ", y);
/*
An EC_KEY represents a public key and (optionaly) an associated private key.
A new EC_KEY (with no associated curve) can be constructed by calling EC_KEY_new. 
The reference count for the newly created EC_KEY is initially set to 1.
A curve can be associated with the EC_KEY by calling EC_KEY_set_group.
Alternatively a new EC_KEY can be constructed by calling EC_KEY_new_by_curve_name
and supplying the nid of the associated curve. Run "openssl ecparam -list_curves" for curve names. 
This function simply wraps calls to EC_KEY_new and EC_GROUP_new_by_curve_name.
*/
// Create the EC key with the curve SECP256R1.
if ((ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) {
fprintf(stderr, "Error creating EC key\n");
goto end;
}
// Create the EC point. EC_KEY_get0_group returns the EC_GROUP associated with the EC_KEY.
if ((ec_point = EC_POINT_new(EC_KEY_get0_group(ec_key))) == NULL) {
fprintf(stderr, "Error creating EC point\n");
goto end;
}
// Set the x and y coordinates of the point
if (!EC_POINT_set_affine_coordinates_GFp(EC_KEY_get0_group(ec_key), ec_point, x, y, NULL)) {
fprintf(stderr, "Error setting EC point coordinates\n");
goto end;
}
// Set the public key for the EC key
if (!EC_KEY_set_public_key(ec_key, ec_point)) {
fprintf(stderr, "Error setting EC public key\n");
goto end;
}
// Assign the EC key to the EVP_PKEY
if ((public_key = EVP_PKEY_new()) == NULL) {
fprintf(stderr, "Error creating EVP_PKEY\n");
goto end;
}
if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
fprintf(stderr, "Error assigning EC key to EVP_PKEY\n");
goto end;
}
// Create the message digest context
if ((md_ctx = EVP_MD_CTX_new()) == NULL) {
fprintf(stderr, "Error creating message digest context\n");
goto end;
}
// Initialize the message digest context with SHA-256 and the public key
if (!EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, public_key)) {
fprintf(stderr, "Error initializing message digest context\n");
goto end;
}
// Update the message digest context with the message
if (!EVP_DigestVerifyUpdate(md_ctx, message, message_len)) {
fprintf(stderr, "Error updating message digest context\n");
goto end;
}
// Verify the signature
result = EVP_DigestVerifyFinal(md_ctx, signature, signature_len);
printf("Result: %d\n", result);
end:
if (result != 1) {
ERR_print_errors_fp(stderr);
}
if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx);
if (public_key != NULL) EVP_PKEY_free(public_key);
if (ec_point != NULL) EC_POINT_free(ec_point);

> if (ec_key != NULL) EC_KEY_free(ec_key); // FIXME: here it crashes

    return result;
}

答案1

得分: 3

如果您查看EVP_PKEY_assign_EC_KEY函数的文档:

> EVP_PKEY_assign_RSA(),EVP_PKEY_assign_DSA(),EVP_PKEY_assign_DH(),
> EVP_PKEY_assign_EC_KEY(),EVP_PKEY_assign_POLY1305()和
> EVP_PKEY_assign_SIPHASH()将引用的密钥设置为key,但是在内部使用提供的密钥,因此在释放父pkey时,key将被释放。

这意味着通过调用EVP_PKEY_assign_EC_KEY,您正在将EC_KEY的“所有权”转让给了EVP_PKEY,当您释放EVP_PKEY(EVP_PKEY_free调用)时,它将为您释放它。因此,调用EC_KEY_free会导致第二次释放,从而导致崩溃。

因此,修复方法如下:

if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
    fprintf(stderr, "Error assigning EC key to EVP_PKEY\n");
    goto end;
}
ec_key = NULL; // public_key现在拥有ec_key,因此我们放弃了对它的控制
英文:

If you look at the documentation for the EVP_PKEY_assign_EC_KEY function:

> EVP_PKEY_assign_RSA(), EVP_PKEY_assign_DSA(), EVP_PKEY_assign_DH(),
> EVP_PKEY_assign_EC_KEY(), EVP_PKEY_assign_POLY1305() and
> EVP_PKEY_assign_SIPHASH() set the referenced key to key however these
> use the supplied key internally and so key will be freed when the
> parent pkey is freed.

This means that by calling EVP_PKEY_assign_EC_KEY you are transfering "ownership" of the EC_KEY to the EVP_PKEY and it will free it for you when you free the EVP_PKEY (EVP_PKEY_free call). So the call to EC_KEY_free is freeing it for a second time causing the crash.

So a fix would be:

if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
fprintf(stderr, "Error assigning EC key to EVP_PKEY\n");
goto end;
}
ec_key = NULL; // public_key now owns ec_key so we give up control of it

huangapple
  • 本文由 发表于 2023年6月2日 12:07:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76387060.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定