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;
}
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