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

Tomas Mraz tomas at openssl.org
Thu Sep 29 06:12:02 UTC 2022


Hi,

comments below.

On Wed, 2022-09-28 at 22:12 +0000, GonzalezVillalobos, Diego wrote:
> [AMD Official Use Only - General]
> 
> Hello Tomas,
> 
> I generated the key as you suggested, and I am no longer getting an
> error message! Thank you for that. Here is how I'm generating the key
> now:
> 
> // SEV certificate are little-endian, must reverse bytes before
> generating key
>             if ((cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA256) ||
>                 (cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA384)) {
>                 //Grab x param and flip bytes to BE
>                 memcpy(px, &cert->pub_key.ecdsa.qx, ec_group_order);
>                 if(!sev::reverse_bytes(px, sizeof(px)))
>                     break;
>                 //Grab y param and flip bytes to BE
>                 memcpy(py, &cert->pub_key.ecdsa.qy, ec_group_order);
>                 if(!sev::reverse_bytes(py, sizeof(py)))
>                     break;
>             }
>             else if ((cert->pub_key_algo ==
> SEV_SIG_ALGO_ECDH_SHA256)  ||
>                     (cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA384))
> {
>                 //Grab x param and flip bytes to BE
>                 memcpy(px, &cert->pub_key.ecdh.qx, ec_group_order);
>                 if(!sev::reverse_bytes(px, sizeof(px)))
>                     break;
>                 //Grab y param and flip bytes to BE
>                 memcpy(py, &cert->pub_key.ecdh.qy, ec_group_order);
>                 if(!sev::reverse_bytes(py, sizeof(py)))
>                     break;
>             }
> 
>             int px_size = sizeof(px)/sizeof(*px);
>             int py_size = sizeof(py)/sizeof(*py);
> 
>             // Will contain x and y components
>             unsigned char public_key_xy[1 + px_size + py_size] = {0};
> 
>             //Add point conversion as first value
>             public_key_xy[0] = POINT_CONVERSION_UNCOMPRESSED;
> 
>             //Add x components after point compression
>             memcpy(public_key_xy + 1, px, px_size);
>             //Add y components after x
>             memcpy(public_key_xy + px_size + 1, py ,py_size);
>             
>             // 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_octet_string(params_build,
> OSSL_PKEY_PARAM_PUB_KEY, public_key_xy, sizeof(public_key_xy))) {
>                 cout << "Error: failed to push qx 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);
> 
>             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;
>                 printf("Failed Final Verify
> %s\n",ERR_error_string(ERR_get_error(),NULL));
>                 break;
>             }
> 
>             if (EVP_PKEY_get_base_id(evp_pub_key) != EVP_PKEY_EC) {
>                 cout << "wrong key type" << endl;
>                 break;
>             }
>         }
> 
>         if (!evp_pub_key) {
>             cout << "no evp pkey" << endl;
>             break;
>         }
>         cout << "compile key succesful" << endl;
>         cmd_ret = STATUS_SUCCESS;
> 
> Although the key generation works and I'm not getting a verify error
> anymore, I am still unsuccessful on verifying the digest. It keeps
> failing (returning 0). Here is how I'm currently trying to do the
> verification.

Are you sure the px_size and py_size is equal to the group order? The x
and y values must be padded to the group order with 0 (at the start
because the values need to be BE).

> 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* der_sig;
> 
>                 // 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); // FreesBIGNUMs manually here
>                     BN_free(r_big_num);
>                     ECDSA_SIG_free(tmp_ecdsa_sig);
>                     break;
>                 }


>                 int der_sig_len = i2d_ECDSA_SIG(tmp_ecdsa_sig, NULL);
>                 der_sig = static_cast<unsigned
> char*>(OPENSSL_malloc(der_sig_len));
>                 unsigned char* der_iter = der_sig;
>                 der_sig_len = i2d_ECDSA_SIG(tmp_ecdsa_sig,
> &der_iter); // <= bugfix here
> 
>                 if (der_sig_len == 0) {
>                     cout << "sig length invalid" << endl;
>                     break;
>                 }
> 
>                 if (der_sig == NULL) {
>                     cout << "sig generation failed" << endl;
>                     break;
>                 }
> 

You do not need to call i2d_ECDSA_SIG() twice. Just assign NULL to
der_iter and i2d_ECDSA_SIG(tmp_ecdsa_sig, &der_iter) call will allocate
the buffer for the encoded signature for you.


>                 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, (uint8_t
> *)child_cert, pub_key_offset) <= 0){    // Calls SHA256_UPDATE
>                     cout << "updating digest fails" << endl;
>                     break;
>                 }
> 
>                 int ret = EVP_DigestVerifyFinal(verify_md_ctx,
> der_sig, der_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;
> 
> Could it be because I'm creating a ECDSA SIG object and then turning
> it into a der format to verify? Again, suggestions would be
> appreciated.

No, that should be correct. The signature as produced or verified by
the EVP_DigestSignFinal or EVP_DigestVerifyFinal should be in the DER
encoding of the ECDSA_SIG. So you have that correct.

I do not see any apparent problem with your code apart of the remarks
above.

-- 
Tomáš Mráz, OpenSSL



More information about the openssl-users mailing list