RE: Updating RSA public key generation and signature verification from 1.1.1 to 3.0
[AMD Official Use Only - General] Hello Tomas, There was a logic error in my code, I did not realize that the first iteration of the verification was supposed to fail. The verification is working correctly! I apologize for my last response. I really appreciate all your help! Thank you very much, Diego Gonzalez -- -Original Message- From: Tomas Mraz Sent: Friday, September 30, 2022 1:22 AM To: GonzalezVillalobos, Diego ; openssl-users@openssl.org Subject: Re: Updating RSA public key generation and signature verification from 1.1.1 to 3.0 Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding. Hi, unfortunately I do not see anything wrong with the code. Does the EVP_DigestVerifyFinal return 0 or negative value? I do not think this is a bug in OpenSSL as this API is thoroughly tested and it is highly improbable that there would be a bug in the ECDSA verification through this API. I am currently out of ideas on what could be wrong or how to investigate further. Perhaps someone else can chime in on what can be wrong? Tomas On Thu, 2022-09-29 at 19:22 +, GonzalezVillalobos, Diego wrote: > [AMD Official Use Only - General] > > Hello Tomas, > > So, I made sure that px_size and py_size are equal to the group order > (48). I was able to verify successfully using our previous method > (deprecated) with the new key generation method, but I'm still not > able to get the digestverify to work successfully. As a reminder this > is how we were verifying before: > > // Determine if SHA_TYPE is 256 bit or 384 bit if > (parent_cert->pub_key_algo == SEV_SIG_ALGO_RSA_SHA256 || > parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA256 ||parent_cert- > >pub_key_algo == SEV_SIG_ALGO_ECDH_SHA256) > { > sha_type = SHA_TYPE_256; > sha_digest = sha_digest_256; > sha_length = sizeof(hmac_sha_256); > } > else if (parent_cert->pub_key_algo == SEV_SIG_ALGO_RSA_SHA384 || > parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA384 || > parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA384) > { > sha_type = SHA_TYPE_384; > sha_digest = sha_digest_384; > sha_length = sizeof(hmac_sha_512); > } > else > { > break; > } > > // 1. SHA256 hash the cert from Version through pub_key > parameters > // Calculate the digest of the input message rsa.c -> > rsa_pss_verify_msg() > // SHA256/SHA384 hash the cert from the [Version:pub_key] > params > uint32_t pub_key_offset = offsetof(sev_cert, sig_1_usage); // > 16 + sizeof(SEV_PUBKEY) > if (!digest_sha((uint8_t *)child_cert, pub_key_offset, > sha_digest, sha_length, sha_type)) { > break; > } > if ((parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA256) || > (parent_cert->pub_key_algo == > SEV_SIG_ALGO_ECDSA_SHA384) || > (parent_cert->pub_key_algo == > SEV_SIG_ALGO_ECDH_SHA256) || > (parent_cert->pub_key_algo == > SEV_SIG_ALGO_ECDH_SHA384)) { // ecdsa.c -> sign_verify_msg > 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;
RE: Updating RSA public key generation and signature verification from 1.1.1 to 3.0
[AMD Official Use Only - General] Hello Tomas, So, I made sure that px_size and py_size are equal to the group order (48). I was able to verify successfully using our previous method (deprecated) with the new key generation method, but I'm still not able to get the digestverify to work successfully. As a reminder this is how we were verifying before: // Determine if SHA_TYPE is 256 bit or 384 bit if (parent_cert->pub_key_algo == SEV_SIG_ALGO_RSA_SHA256 || parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA256 ||parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA256) { sha_type = SHA_TYPE_256; sha_digest = sha_digest_256; sha_length = sizeof(hmac_sha_256); } else if (parent_cert->pub_key_algo == SEV_SIG_ALGO_RSA_SHA384 || parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA384 || parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA384) { sha_type = SHA_TYPE_384; sha_digest = sha_digest_384; sha_length = sizeof(hmac_sha_512); } else { break; } // 1. SHA256 hash the cert from Version through pub_key parameters // Calculate the digest of the input message rsa.c -> rsa_pss_verify_msg() // SHA256/SHA384 hash the cert from the [Version:pub_key] params uint32_t pub_key_offset = offsetof(sev_cert, sig_1_usage); // 16 + sizeof(SEV_PUBKEY) if (!digest_sha((uint8_t *)child_cert, pub_key_offset, sha_digest, sha_length, sha_type)) { break; } if ((parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA256) || (parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA384) || (parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA256) || (parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDH_SHA384)) { // ecdsa.c -> sign_verify_msg 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; } Digest sha function: bool digest_sha(const void *msg, size_t msg_len, uint8_t *digest, size_t digest_len, SHA_TYPE sha_type) { bool ret = false; do {//TODO 384 vs 512 is all a mess if ((sha_type == SHA_TYPE_256 && digest_len != SHA256_DIGEST_LENGTH)/* || (sha_type == SHA_TYPE_384 && digest_len != SHA384_DIGEST_LENGTH)*/) break; if (sha_type == SHA_TYPE_256) { SHA256_CTX context; if (SHA256_Init() != 1) break; if (SHA256_Update(, (void *)msg, msg_len) != 1) break; if (SHA256_Final(digest, ) != 1) break; } else if (sha_type == SHA_TYPE_384) { SHA512_CTX context; if (SHA384_Init() != 1) break; if (SHA384_Update(, (void *)msg, msg_len) != 1) break; if (SHA384_Final(digest, ) != 1) break; } ret = true; } while (0); return ret; } This works using the new EC EVP key generation. The current verification method keeps failing: if ((parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA256) || (parent_cert->pub_key_algo == SEV_SIG_ALGO_ECDSA_SHA384) || (parent_cert->pub_key_algo ==
RE: Updating RSA public key generation and signature verification from 1.1.1 to 3.0
// 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, ); > > > 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:0395: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 > Sent: Friday, September 9, 2022 10:36 AM > To: GonzalezVillalobos, Diego ; > openssl-users@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 +, 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 -- Tomáš Mráz, OpenSSL
RE: Updating RSA public key generation and signature verification from 1.1.1 to 3.0
lt;< "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:0395: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 Sent: Friday, September 9, 2022 10:36 AM To: GonzalezVillalobos, Diego ; openssl-users@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 +, 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
Updating RSA public key generation and signature verification from 1.1.1 to 3.0
[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. //OPENSSL 1.1.1 Code SEV_ERROR_CODE AMDCert::amd_cert_validate_sig(const amd_cert *cert, const amd_cert *parent, ePSP_DEVICE_TYPE device_type) { SEV_ERROR_CODE cmd_ret = ERROR_INVALID_CERTIFICATE; hmac_sha_256 sha_digest_256; hmac_sha_512 sha_digest_384; SHA_TYPE algo = SHA_TYPE_256; uint8_t *sha_digest = NULL; size_t sha_length = 0; RSA *rsa_pub_key = NULL; BIGNUM *modulus = NULL; BIGNUM *pub_exp = NULL; EVP_MD_CTX* md_ctx = NULL; uint32_t sig_len = cert->modulus_size/8; uint32_t digest_len = 0; uint8_t decrypted[AMD_CERT_KEY_BYTES_4K] = {0}; // TODO wrong length uint8_t signature[AMD_CERT_KEY_BYTES_4K] = {0}; uint32_t fixed_offset = offsetof(amd_cert, pub_exp);// 64 bytes do { if (!cert || !parent) { cmd_ret = ERROR_INVALID_PARAM; break; } // Set SHA_TYPE to 256 bit or 384 bit depending on device_type if (device_type == PSP_DEVICE_TYPE_NAPLES) { algo = SHA_TYPE_256; sha_digest = sha_digest_256; sha_length = sizeof(hmac_sha_256); } else /*if (ROME/MILAN)*/ { algo = SHA_TYPE_384; sha_digest = sha_digest_384; sha_length = sizeof(hmac_sha_512); } // Memzero all the buffers memset(sha_digest, 0, sha_length); memset(decrypted, 0, sizeof(decrypted)); memset(signature, 0, sizeof(signature)); // New up the RSA key rsa_pub_key = RSA_new(); // Convert the parent to an RSA key to pass into RSA_verify modulus = BN_lebin2bn((uint8_t *)>modulus, parent->modulus_size/8, NULL); // n// New's up BigNum pub_exp = BN_lebin2bn((uint8_t *)>pub_exp, parent->pub_exp_size/8, NULL); // e if (RSA_set0_key(rsa_pub_key, modulus, pub_exp, NULL) != 1) break; // Create digest from certificate md_ctx = EVP_MD_CTX_create(); if (EVP_DigestInit(md_ctx, (algo == SHA_TYPE_256) ? EVP_sha256() : EVP_sha384()) <= 0) break; if (EVP_DigestUpdate(md_ctx, cert, fixed_offset) <= 0) // Calls SHA256_UPDATE break; if (EVP_DigestUpdate(md_ctx, >pub_exp, cert->pub_exp_size/8) <= 0) break; if (EVP_DigestUpdate(md_ctx, >modulus, cert->modulus_size/8) <= 0) break; EVP_DigestFinal(md_ctx, sha_digest, _len); // Swap the bytes of the signature memcpy(signature, >sig, parent->modulus_size/8); if (!sev::reverse_bytes(signature, parent->modulus_size/8)) break; // Now we will verify the signature. Start by a RAW decrypt of the signature if (RSA_public_decrypt(sig_len, signature, decrypted, rsa_pub_key, RSA_NO_PADDING) == -1) break; // Verify the data // SLen of -2 means salt length is recovered from the signature if (RSA_verify_PKCS1_PSS(rsa_pub_key, sha_digest, (algo == SHA_TYPE_256) ? EVP_sha256() : EVP_sha384(), decrypted, -2) != 1) { break; } cmd_ret = STATUS_SUCCESS; } while (0); // Free the keys and contexts if (rsa_pub_key) RSA_free(rsa_pub_key); if (md_ctx) EVP_MD_CTX_free(md_ctx); return cmd_ret; } My current attempt to do this update is first creating an EVP_PKEY object which will now store the public key and inputting the same data from parent by using the EVP_PKEY_fromdata generation function. I build my OSSL_PARAMETERS using OSSL_PARAM_BLD to which I push the data from parent. Lastly for the verification I am using the EVP_DigestVerifyFinal procedure. I create the digest using EVP_DigestVerifyInit and EVP_DigestVerifyUpdate and passing in the same data from cert that we were using in the 1.1.1 version of the function. I have tried different approaches to both creating the key and the verification procedure, but I cannot seem to get it to work. // OPENSSL 3.0 code SEV_ERROR_CODE