Updating RSA public key generation and signature verification from 1.1.1 to 3.0

GonzalezVillalobos, Diego Diego.GonzalezVillalobos at amd.com
Thu Sep 22 19:15:16 UTC 2022


[AMD Official Use Only - General]

Hello Tomas,

Thank you for your response. Thanks to the example you guided me towards I was able to get the verification to work. But now I am stuck on a similar issue, but now I am trying to generate an EC key. 

This is how we used to generate and verify the EC public key from raw data:
Generation:

            // Store the x and y components as separate BIGNUM objects. The values in the
            // SEV certificate are little-endian, must reverse bytes before storing in BIGNUM
            if ((cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA256) ||
                (cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA384)) {
                x_big_num = BN_lebin2bn(cert->pub_key.ecdsa.qx, sizeof(cert->pub_key.ecdsa.qx), NULL);  // New's up BigNum
                y_big_num = BN_lebin2bn(cert->pub_key.ecdsa.qy, sizeof(cert->pub_key.ecdsa.qy), NULL);
            }
            else if ((cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA256)  ||
                    (cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA384)) {
                x_big_num = BN_lebin2bn(cert->pub_key.ecdh.qx, sizeof(cert->pub_key.ecdh.qx), NULL);  // New's up BigNum
                y_big_num = BN_lebin2bn(cert->pub_key.ecdh.qy, sizeof(cert->pub_key.ecdh.qy), NULL);
            }

            int nid = EC_curve_nist2nid("P-384");   // NID_secp384r1

            // Create/allocate memory for an EC_KEY object using the NID above
            if (!(ec_pub_key = EC_KEY_new_by_curve_name(nid)))
                break;
            // Store the x and y coordinates of the public key
            if (EC_KEY_set_public_key_affine_coordinates(ec_pub_key, x_big_num, y_big_num) != 1)
                break;
            // Make sure the key is good
            if (EC_KEY_check_key(ec_pub_key) != 1)
                break;

            /*
             * Create a public EVP_PKEY from the public EC_KEY
             * This function links evp_pub_key to ec_pub_key, so when evp_pub_key
             *  is freed, ec_pub_key is freed. We don't want the user to have to
             *  manage 2 keys, so just return EVP_PKEY and make sure user free's it
             */
            if (EVP_PKEY_assign_EC_KEY(evp_pub_key, ec_pub_key) != 1)
                break;
/*Generation successful*/

Verification:

ECDSA_SIG *tmp_ecdsa_sig = ECDSA_SIG_new();
                BIGNUM *r_big_num = BN_new();
                BIGNUM *s_big_num = BN_new();

                // Store the x and y components as separate BIGNUM objects. The values in the
                // SEV certificate are little-endian, must reverse bytes before storing in BIGNUM
                r_big_num = BN_lebin2bn(cert_sig[i].ecdsa.r, sizeof(sev_ecdsa_sig::r), r_big_num);    // LE to BE
                s_big_num = BN_lebin2bn(cert_sig[i].ecdsa.s, sizeof(sev_ecdsa_sig::s), s_big_num);

                // Calling ECDSA_SIG_set0() transfers the memory management of the values to
                // the ECDSA_SIG object, and therefore the values that have been passed
                // in should not be freed directly after this function has been called
                if (ECDSA_SIG_set0(tmp_ecdsa_sig, r_big_num, s_big_num) != 1) {
                    BN_free(s_big_num);                   // Frees BIGNUMs manually here
                    BN_free(r_big_num);
                    ECDSA_SIG_free(tmp_ecdsa_sig);
                    continue;
                }
                EC_KEY *tmp_ec_key = EVP_PKEY_get1_EC_KEY(parent_signing_key); // Make a local key so you can free it later
                if (ECDSA_do_verify(sha_digest, (uint32_t)sha_length, tmp_ecdsa_sig, tmp_ec_key) != 1) {
                    EC_KEY_free(tmp_ec_key);
                    ECDSA_SIG_free(tmp_ecdsa_sig);      // Frees BIGNUMs too
                    continue;
                }

                found_match = true;
                EC_KEY_free(tmp_ec_key);
                ECDSA_SIG_free(tmp_ecdsa_sig);      // Frees BIGNUMs too
                break;
            }
/*Verification successful*/

This is my current attempt for public key generation and verification:
Generation:
/ Store the x and y components as separate BIGNUM objects. The values in the
            // SEV certificate are little-endian, must reverse bytes before storing in BIGNUM
            if ((cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA256) ||
                (cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA384)) {
                x_big_num = BN_lebin2bn(cert->pub_key.ecdsa.qx, sizeof(cert->pub_key.ecdsa.qx), NULL);  // New's up BigNum
                y_big_num = BN_lebin2bn(cert->pub_key.ecdsa.qy, sizeof(cert->pub_key.ecdsa.qy), NULL);
            }
            else if ((cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA256)  ||
                    (cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA384)) {
                x_big_num = BN_lebin2bn(cert->pub_key.ecdh.qx, sizeof(cert->pub_key.ecdh.qx), NULL);  // New's up BigNum
                y_big_num = BN_lebin2bn(cert->pub_key.ecdh.qy, sizeof(cert->pub_key.ecdh.qy), NULL);
            }

            int nid = EC_curve_nist2nid("P-384");   // NID_secp384r1

            OSSL_PARAM_BLD *params_build = OSSL_PARAM_BLD_new();

            if ( params_build == NULL ) {
                cout << "Params build fails" << endl;
                break;
            }

            if (!OSSL_PARAM_BLD_push_utf8_string(params_build, OSSL_PKEY_PARAM_GROUP_NAME, "P-384", 0)) {
                cout<< "Push EC curve to build fails" << endl;
                break;
            }

            if (!OSSL_PARAM_BLD_push_BN(params_build, OSSL_PKEY_PARAM_EC_PUB_X, x_big_num)) {
                cout << "Error: failed to push qx into param build." << endl;
                break;
            }

        
            if (!OSSL_PARAM_BLD_push_BN(params_build, OSSL_PKEY_PARAM_EC_PUB_Y, y_big_num)) {
                cout << "Error: failed to push qy into param build." << endl;
                break;
            }
        
            OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(params_build);

            if ( params == NULL ) {
                cout << "Error: building parameters." << endl;
                break;
            }

            OSSL_PARAM_BLD_free(params_build);
        
            key_gen_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
            EVP_PKEY_CTX_set_ec_paramgen_curve_nid(key_gen_ctx, NID_secp384r1);

            if(EVP_PKEY_fromdata_init(key_gen_ctx) != 1) {
                cout << "failed to initialize key creation." << endl;
                break;
            }

            if(EVP_PKEY_fromdata(key_gen_ctx, &evp_pub_key, EVP_PKEY_PUBLIC_KEY, params) != 1) {
                cout << "key generation breaks" << endl;
                break;
            }

            if (EVP_PKEY_get_base_id(evp_pub_key) != EVP_PKEY_EC) {
                cout << "wrong key type" << endl;
                break;
            }
/*Generation successful*/

Verification:
                ECDSA_SIG *tmp_ecdsa_sig = ECDSA_SIG_new();
                BIGNUM *r_big_num = BN_new();
                BIGNUM *s_big_num = BN_new();
                uint32_t sig_len;
                unsigned char *p;

                // Store the x and y components as separate BIGNUM objects. The values in the
                // SEV certificate are little-endian, must reverse bytes before storing in BIGNUM
                r_big_num = BN_lebin2bn(cert_sig[i].ecdsa.r, sizeof(sev_ecdsa_sig::r), r_big_num);    // LE to BE
                s_big_num = BN_lebin2bn(cert_sig[i].ecdsa.s, sizeof(sev_ecdsa_sig::s), s_big_num);

                // Calling ECDSA_SIG_set0() transfers the memory management of the values to
                // the ECDSA_SIG object, and therefore the values that have been passed
                // in should not be freed directly after this function has been called
                if (ECDSA_SIG_set0(tmp_ecdsa_sig, r_big_num, s_big_num) != 1) {
                    BN_free(s_big_num);                   // Frees BIGNUMs manually here
                    BN_free(r_big_num);
                    ECDSA_SIG_free(tmp_ecdsa_sig);
                    break;
                }

                sig_len = i2d_ECDSA_SIG(tmp_ecdsa_sig, NULL);
		        unsigned char signature[sig_len];

                p = signature;

		        sig_len = i2d_ECDSA_SIG(tmp_ecdsa_sig, &p);


                if (signature == NULL) {
			        cout << "sig mem failed" << endl;
                    break;
                }

		        if (sig_len == 0)
                    cout << "sig length invalid" << endl;

                verify_md_ctx = EVP_MD_CTX_new();


                if (!verify_md_ctx) {
                    cout << "Error md verify context " << endl;;
                    break;
                }

                if (EVP_DigestVerifyInit(verify_md_ctx, NULL, (parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA256 || parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA256) ? EVP_sha256() : EVP_sha384(), NULL, parent_signing_key) <= 0) {
                    cout << "Init fails " << endl;
                    break;
                }

                if (EVP_DigestVerifyUpdate(verify_md_ctx, child_cert, pub_key_offset) <= 0){    // Calls SHA256_UPDATE
                    cout << "updating digest fails" << endl;
                    break;
                }

                int ret = EVP_DigestVerifyFinal(verify_md_ctx, signature, sig_len);
                cout << ret << endl;
                if (ret == 0) {
                    cout << "EC Verify digest fails" << endl;
                    break; 
                } else if (ret < 0) {
                    printf("Failed Final Verify %s\n",ERR_error_string(ERR_get_error(),NULL));
                    cout << "EC Verify error" << endl;
                    break;
                }

                found_match = true;
                cout << "SEV EC verification Succesful" << endl;


My current output when I reach EVP_DigestVerifyFinal is showing this error:
Failed Final Verify error:03000095:digital envelope routines::no operation set

I have been playing around with it for a while, but I am stuck at this point. Any advice would be appreciated.

Thank you,

Diego Gonzalez Villalobos
----------------------------------------------------------------------------------------------------------------------------------
 

-----Original Message-----
From: Tomas Mraz <tomas at openssl.org> 
Sent: Friday, September 9, 2022 10:36 AM
To: GonzalezVillalobos, Diego <Diego.GonzalezVillalobos at amd.com>; openssl-users at openssl.org
Subject: Re: Updating RSA public key generation and signature verification from 1.1.1 to 3.0

[CAUTION: External Email]

On Thu, 2022-09-08 at 16:10 +0000, GonzalezVillalobos, Diego via openssl-users wrote:
> [AMD Official Use Only - General]
>
> Hello everyone,
>
> I am currently working on updating a signature verification function 
> in C++ and I am a bit stuck. I am trying to replace the deprecated
> 1.1.1 functions to the appropriate 3.0 versions. The function takes in 
> 2 certificate objects (parent and cert), which are not x509 
> certificates, but certificates the company had previously defined.
> Using the contents from parent we create an RSA public key and using 
> the contents from cert we create the digest and grab the signature to 
> verify.
>
> In the 1.1.1 version we were using the RSA Object and the rsa_set0_key 
> function to create the RSA public key and then used RSA_public_decrypt 
> to decrypt the signature and RSA_verify_PKCS1_PSS to verify it. This 
> whole workflow is now deprecated.
>
...
> Is this the correct way of creating RSA keys now? Where is my logic 
> failing? Can the same type of procedure even be done on 3.0? Any 
> advice would be really appreciated.
>

In the original code you seem to be using PSS padding for verification.
Did you try to set the PSS padding on the digest verify context? See demos/signature/rsa_pss_hash.c on how to do it.

--
Tomáš Mráz, OpenSSL


More information about the openssl-users mailing list