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

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

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

问题

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

  1. // 我有一个 EC 公钥坐标,分别为 "x" 和 "y",它们是 64 字节的字符串。
  2. // 我有一条消息,必须使用我拥有的签名进行验证。
  3. // 我编写了下面显示的函数。在释放密钥时,该函数崩溃,即在使用行 EC_KEY_free(ec_key) 时。
  4. // 如果我注释掉这一行,就不会出现崩溃。请提供您的意见/提示,为什么这一行会导致问题。感谢您的指导。
  5. // 例如,公钥 "x" 和 "y" 的值如下:
  6. // x 为 5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE
  7. // y 为 C05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1
  8. // 在使用上述值后,我拥有的 BIGNUM 打印值如下:
  9. // BIGNUM x 坐标:0x5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE
  10. // BIGNUM y 坐标:0xC05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1
  11. 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) {
  12. int result = 0;
  13. EC_KEY* ec_key = NULL;
  14. EC_POINT* ec_point = NULL;
  15. EVP_PKEY* public_key = NULL;
  16. EVP_MD_CTX* md_ctx = NULL;
  17. // 解析公钥
  18. BIGNUM* x = BN_new();
  19. BIGNUM* y = BN_new();
  20. if (!BN_hex2bn(&x, public_key_x)) {
  21. perror("解析公钥 x 坐标时出错");
  22. return -1;
  23. }
  24. printBN("BIGNUM x 坐标:", x);
  25. if (!BN_hex2bn(&y, public_key_y)) {
  26. perror("解析公钥 y 坐标时出错");
  27. return -1;
  28. }
  29. printBN("BIGNUM y 坐标:", y);
  30. /*
  31. EC_KEY 代表公钥和(可选的)相关私钥。
  32. 可以通过调用 EC_KEY_new 来构建一个新的 EC_KEY(没有关联的曲线)。
  33. 新创建的 EC_KEY 的引用计数最初设置为 1。
  34. 可以通过调用 EC_KEY_set_group 来将曲线与 EC_KEY 关联起来。
  35. 或者,可以通过调用 EC_KEY_new_by_curve_name 来构建一个新的 EC_KEY,
  36. 并提供关联曲线的 nid(命名标识符)。
  37. 运行 "openssl ecparam -list_curves" 查看曲线名称。
  38. 该函数简单地包装了对 EC_KEY_new 和 EC_GROUP_new_by_curve_name 的调用。
  39. */
  40. // 使用曲线 SECP256R1 创建 EC 密钥。
  41. if ((ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) {
  42. fprintf(stderr, "创建 EC 密钥时出错\n");
  43. goto end;
  44. }
  45. // 创建 EC 点。EC_KEY_get0_group 返回与 EC_KEY 关联的 EC_GROUP。
  46. if ((ec_point = EC_POINT_new(EC_KEY_get0_group(ec_key))) == NULL) {
  47. fprintf(stderr, "创建 EC 点时出错\n");
  48. goto end;
  49. }
  50. // 设置点的 x 和 y 坐标
  51. if (!EC_POINT_set_affine_coordinates_GFp(EC_KEY_get0_group(ec_key), ec_point, x, y, NULL)) {
  52. fprintf(stderr, "设置 EC 点坐标时出错\n");
  53. goto end;
  54. }
  55. // 设置 EC 密钥的公钥
  56. if (!EC_KEY_set_public_key(ec_key, ec_point)) {
  57. fprintf(stderr, "设置 EC 公钥时出错\n");
  58. goto end;
  59. }
  60. // 将 EC 密钥分配给 EVP_PKEY
  61. if ((public_key = EVP_PKEY_new()) == NULL) {
  62. fprintf(stderr, "创建 EVP_PKEY 时出错\n");
  63. goto end;
  64. }
  65. if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
  66. fprintf(stderr, "将 EC 密钥分配给 EVP_PKEY 时出错\n");
  67. goto end;
  68. }
  69. // 创建消息摘要上下文
  70. if ((md_ctx = EVP_MD_CTX_new()) == NULL) {
  71. fprintf(stderr, "创建消息摘要上下文时出错\n");
  72. goto end;
  73. }
  74. // 使用 SHA-256 和公钥初始化消息摘要上下文
  75. if (!EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, public_key)) {
  76. fprintf(stderr, "初始化消息摘要上下文时出错\n");
  77. goto end;
  78. }
  79. // 使用消息更新消息摘要上下文
  80. if (!EVP_DigestVerifyUpdate(md_ctx, message, message_len)) {
  81. fprintf(stderr, "更新消息摘要上下文时出错\n");
  82. goto end;
  83. }
  84. // 验证签名
  85. result = EVP_DigestVerifyFinal(md_ctx, signature, signature_len);
  86. printf("结果:%d\n", result);
  87. end:
  88. if (result != 1) {
  89. ERR_print_errors_fp(stderr);
  90. }
  91. if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx);
  92. if (public_key != NULL) EVP_PKEY_free(public_key);
  93. if (ec_point != NULL) EC_POINT_free(ec_point);
  94. // 如果 (ec_key != NULL) 则释放 EC_KEY(FIXME: 这里导致崩溃)
  95. if (ec_key != NULL) EC_KEY_free(ec_key);
  96. return result;
  97. }

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

英文:

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

  1. x is 5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE
  2. y is C05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1

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

  1. BIGNUM x coordinate: 0x5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE
  2. BIGNUM y coordinate: 0xC05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1
  3. 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) {
  4. int result = 0;
  5. EC_KEY* ec_key = NULL;
  6. EC_POINT* ec_point = NULL;
  7. EVP_PKEY* public_key = NULL;
  8. EVP_MD_CTX* md_ctx = NULL;
  9. // parse public key
  10. BIGNUM* x = BN_new();
  11. BIGNUM* y = BN_new();
  12. if (!BN_hex2bn(&x, public_key_x)) {
  13. perror("Error parsing public key x coordinate");
  14. return -1;
  15. }
  16. printBN("BIGNUM x coordinate: ", x);
  17. if (!BN_hex2bn(&y, public_key_y)) {
  18. perror("Error parsing public key y coordinate");
  19. return -1;
  20. }
  21. printBN("BIGNUM y coordinate: ", y);
  22. /*
  23. An EC_KEY represents a public key and (optionaly) an associated private key.
  24. A new EC_KEY (with no associated curve) can be constructed by calling EC_KEY_new.
  25. The reference count for the newly created EC_KEY is initially set to 1.
  26. A curve can be associated with the EC_KEY by calling EC_KEY_set_group.
  27. Alternatively a new EC_KEY can be constructed by calling EC_KEY_new_by_curve_name
  28. and supplying the nid of the associated curve. Run "openssl ecparam -list_curves" for curve names.
  29. This function simply wraps calls to EC_KEY_new and EC_GROUP_new_by_curve_name.
  30. */
  31. // Create the EC key with the curve SECP256R1.
  32. if ((ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) {
  33. fprintf(stderr, "Error creating EC key\n");
  34. goto end;
  35. }
  36. // Create the EC point. EC_KEY_get0_group returns the EC_GROUP associated with the EC_KEY.
  37. if ((ec_point = EC_POINT_new(EC_KEY_get0_group(ec_key))) == NULL) {
  38. fprintf(stderr, "Error creating EC point\n");
  39. goto end;
  40. }
  41. // Set the x and y coordinates of the point
  42. if (!EC_POINT_set_affine_coordinates_GFp(EC_KEY_get0_group(ec_key), ec_point, x, y, NULL)) {
  43. fprintf(stderr, "Error setting EC point coordinates\n");
  44. goto end;
  45. }
  46. // Set the public key for the EC key
  47. if (!EC_KEY_set_public_key(ec_key, ec_point)) {
  48. fprintf(stderr, "Error setting EC public key\n");
  49. goto end;
  50. }
  51. // Assign the EC key to the EVP_PKEY
  52. if ((public_key = EVP_PKEY_new()) == NULL) {
  53. fprintf(stderr, "Error creating EVP_PKEY\n");
  54. goto end;
  55. }
  56. if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
  57. fprintf(stderr, "Error assigning EC key to EVP_PKEY\n");
  58. goto end;
  59. }
  60. // Create the message digest context
  61. if ((md_ctx = EVP_MD_CTX_new()) == NULL) {
  62. fprintf(stderr, "Error creating message digest context\n");
  63. goto end;
  64. }
  65. // Initialize the message digest context with SHA-256 and the public key
  66. if (!EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, public_key)) {
  67. fprintf(stderr, "Error initializing message digest context\n");
  68. goto end;
  69. }
  70. // Update the message digest context with the message
  71. if (!EVP_DigestVerifyUpdate(md_ctx, message, message_len)) {
  72. fprintf(stderr, "Error updating message digest context\n");
  73. goto end;
  74. }
  75. // Verify the signature
  76. result = EVP_DigestVerifyFinal(md_ctx, signature, signature_len);
  77. printf("Result: %d\n", result);
  78. end:
  79. if (result != 1) {
  80. ERR_print_errors_fp(stderr);
  81. }
  82. if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx);
  83. if (public_key != NULL) EVP_PKEY_free(public_key);
  84. if (ec_point != NULL) EC_POINT_free(ec_point);

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

  1. return result;
  2. }

答案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会导致第二次释放,从而导致崩溃。

因此,修复方法如下:

  1. if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
  2. fprintf(stderr, "Error assigning EC key to EVP_PKEY\n");
  3. goto end;
  4. }
  5. 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:

  1. if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
  2. fprintf(stderr, "Error assigning EC key to EVP_PKEY\n");
  3. goto end;
  4. }
  5. 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:

确定