From: Craig Lorentzen Date: Fri, 24 Apr 2026 17:25:29 +0000 (+0000) Subject: Map rsaesOaep SubjectPublicKeyInfo to RSA X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cc5dd4ff66ff0cb000a15396620e9c00714d6f64;p=thirdparty%2Fopenssl.git Map rsaesOaep SubjectPublicKeyInfo to RSA TPM 1.2 Endorsement Key certificates use id-RSAES-OAEP (NID_rsaesOaep) as their SubjectPublicKeyInfo algorithm identifier per TCG Credential Profiles V1.2 section 3.2.7. The underlying key is a standard RSAPublicKey. Without this mapping, X509_get_pubkey() fails with a decode error and X509_verify_cert() cannot validate these certificates. Add NID_rsaesOaep handling to the three SPKI decode paths, each of which points at the other two so future changes stay in sync: - x509_pubkey_decode(): remap the NID to NID_rsaEncryption for the legacy ameth lookup. This path is reached via d2i_RSA_PUBKEY()/ossl_d2i_PUBKEY_legacy(), which is in turn invoked by the provider RSA decoder's rsa_d2i_PUBKEY, so it is load-bearing even when the provider path is in use. - x509_pubkey_ex_d2i_ex(): use "RSA" as the decoder keytype name so OSSL_DECODER_CTX_new_for_pkey() selects the RSA provider decoder. The NID check precedes OBJ_obj2txt() so the text conversion is skipped when unused. - ossl_spki2typespki_der_decode(): same remap in the SPKI-to-type-SPKI provider decoder chain. Flatten the existing SM2 special case while here: the original code relied on a dangling else across the #endif, which made the rsaesOaep branch awkward to add. The new structure initializes dataname to empty, applies each special case in turn, and falls back to OBJ_obj2txt() only when no override applied. strcpy() is replaced with OPENSSL_strlcpy() for consistency with surrounding code. The OAEP AlgorithmIdentifier parameters (which carry a TCG-specific pSourceAlgorithm "TCPA" for TPM EKs) are deliberately not interpreted; only the RSAPublicKey body is consumed. Add a test using a real TPM 1.2 EK certificate. The test exercises both the provider decoder path (via X509_from_strings + X509_get0_pubkey) and, when deprecated APIs are available, the legacy path (via d2i_RSA_PUBKEY), confirming the key decodes to an RSA EVP_PKEY of the expected size. Reviewed-by: Tomas Mraz Reviewed-by: Dmitry Belyavskiy MergeDate: Sun May 3 14:44:24 2026 (Merged from https://github.com/openssl/openssl/pull/30961) --- diff --git a/CHANGES.md b/CHANGES.md index 049c0e72887..e8c21677460 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,15 @@ OpenSSL Releases ### Changes between 4.0 and 4.1 [xx XXX xxxx] + * SubjectPublicKeyInfo blobs whose AlgorithmIdentifier uses id-RSAES-OAEP + (NID_rsaesOaep, 1.2.840.113549.1.1.7) with a plain RSAPublicKey body + are now decoded as RSA keys. This is required for interoperability + with TPM 1.2 Endorsement Key certificates per TCG Credential Profiles + V1.2 section 3.2.7. The OAEP AlgorithmIdentifier parameters are not + interpreted. + + *Craig Lorentzen* + * Added test framework for testing function memory allocation failures. *Jakub Zelenka* diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c index d085d749922..78eba3707a5 100644 --- a/crypto/x509/x_pubkey.c +++ b/crypto/x509/x_pubkey.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -199,8 +199,19 @@ static int x509_pubkey_ex_d2i_ex(ASN1_VALUE **pval, } p = in_saved; - if (OBJ_obj2txt(txtoidname, sizeof(txtoidname), - pubkey->algor->algorithm, 0) + /* + * TPM 1.2 Endorsement Key certificates use NID_rsaesOaep in the + * SPKI AlgorithmIdentifier with a plain RSAPublicKey body, per + * TCG Credential Profiles V1.2 section 3.2.7. Map the OID to + * "RSA" here so the provider decoder is selected; the OAEP + * AlgorithmIdentifier parameters are not interpreted. Keep + * this in sync with x509_pubkey_decode() and + * ossl_spki2typespki_der_decode(). + */ + if (OBJ_obj2nid(pubkey->algor->algorithm) == NID_rsaesOaep) { + OPENSSL_strlcpy(txtoidname, "RSA", sizeof(txtoidname)); + } else if (OBJ_obj2txt(txtoidname, sizeof(txtoidname), + pubkey->algor->algorithm, 0) <= 0) { ERR_clear_last_mark(); goto end; @@ -409,6 +420,16 @@ static int x509_pubkey_decode(EVP_PKEY **ppkey, const X509_PUBKEY *key) if (!key->flag_force_legacy) return 0; + /* + * NID_rsaesOaep uses the same underlying RSAPublicKey body as + * NID_rsaEncryption (TCG Credential Profiles V1.2 section 3.2.7). + * Remap so EVP_PKEY_set_type() below finds the RSA ameth. Keep + * this in sync with x509_pubkey_ex_d2i_ex() and + * ossl_spki2typespki_der_decode(). + */ + if (nid == NID_rsaesOaep) + nid = NID_rsaEncryption; + pkey = EVP_PKEY_new(); if (pkey == NULL) { ERR_raise(ERR_LIB_X509, ERR_R_EVP_LIB); diff --git a/providers/implementations/encode_decode/decode_spki2typespki.c b/providers/implementations/encode_decode/decode_spki2typespki.c index ad1fd0ea3e6..6fd680c23f6 100644 --- a/providers/implementations/encode_decode/decode_spki2typespki.c +++ b/providers/implementations/encode_decode/decode_spki2typespki.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2020-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -122,14 +122,29 @@ int ossl_spki2typespki_der_decode(unsigned char *der, long len, int selection, goto end; X509_ALGOR_get0(&oid, NULL, NULL, algor); + /* + * Resolve the SPKI AlgorithmIdentifier OID to the key type name expected + * by the downstream provider decoder. Most OIDs map one-to-one to a key + * type via OBJ_obj2txt(), but a few need special handling: + * + * - SM2 abuses id-ecPublicKey, so the EC parameters must be inspected + * to tell EC and SM2 apart. + * - TPM 1.2 Endorsement Key certificates use NID_rsaesOaep with a + * plain RSAPublicKey body per TCG Credential Profiles V1.2 section + * 3.2.7; the OAEP AlgorithmIdentifier parameters are not interpreted + * here. Keep this in sync with x509_pubkey_decode() and + * x509_pubkey_ex_d2i_ex() in crypto/x509/x_pubkey.c. + */ + dataname[0] = '\0'; #ifndef OPENSSL_NO_EC - /* SM2 abuses the EC oid, so this could actually be SM2 */ if (OBJ_obj2nid(oid) == NID_X9_62_id_ecPublicKey && ossl_x509_algor_is_sm2(algor)) - strcpy(dataname, "SM2"); - else + OPENSSL_strlcpy(dataname, "SM2", sizeof(dataname)); #endif - if (OBJ_obj2txt(dataname, sizeof(dataname), oid, 0) <= 0) + if (dataname[0] == '\0' && OBJ_obj2nid(oid) == NID_rsaesOaep) + OPENSSL_strlcpy(dataname, "RSA", sizeof(dataname)); + if (dataname[0] == '\0' + && OBJ_obj2txt(dataname, sizeof(dataname), oid, 0) <= 0) goto end; ossl_X509_PUBKEY_INTERNAL_free(xpub); diff --git a/test/x509_test.c b/test/x509_test.c index f5f5cc586a0..5ce8a647c07 100644 --- a/test/x509_test.c +++ b/test/x509_test.c @@ -445,6 +445,96 @@ err: return ret; } +/* + * TPM 1.2 Endorsement Key certificate with a NID_rsaesOaep + * SubjectPublicKeyInfo AlgorithmIdentifier (per TCG Credential + * Profiles V1.2 section 3.2.7). The AlgorithmIdentifier carries + * a TCG-specific pSourceAlgorithm ("TCPA") in its parameters, + * which we deliberately do not interpret. The key body itself + * is a standard RSAPublicKey. + */ +static const char *kRsaesOaepCert[] = { + "-----BEGIN CERTIFICATE-----\n", + "MIIDhDCCAmygAwIBAgIUBchBXcXPAWxNMJEsLXEXHv/eVZswDQYJKoZIhvcNAQEL\n", + "BQAwVTELMAkGA1UEBhMCQ0gxHjAcBgNVBAoTFVNUTWljcm9lbGVjdHJvbmljcyBO\n", + "VjEmMCQGA1UEAxMdU1RNIFRQTSBFSyBJbnRlcm1lZGlhdGUgQ0EgMDIwHhcNMjEw\n", + "OTA0MDAwMDAwWhcNMzEwOTA0MDAwMDAwWjAAMIIBNzAiBgkqhkiG9w0BAQcwFaIT\n", + "MBEGCSqGSIb3DQEBCQQEVENQQQOCAQ8AMIIBCgKCAQEAxpd3DnecpD87acEsYp4J\n", + "stM2q5Ss3CkjAP2Ei8yGjbO6DG/6WBIZjTdI5RfIcInoqN4QMso94vm8VqijdRI+\n", + "Zo5hLTCPLKXYwa6UG5yIPZ3ENQdhgZWeEPWe+pp9VUwz8wi78Ifk+CCV6Xp/5kQi\n", + "DCsR+RYbOVb9QgR6kjq+cx1z8YFp5u+k3Pl9tMq9xgIp5E6hT2MaS12KnoN8+hYI\n", + "mfCYVnpzBeQaHDp1KUoyDK6xGt86VxB0QyRbniHI38qgQL6qhO7z96aQ0pNGoQde\n", + "QUxFf/sETurQ5zSf+3btnS8afjxdVBKzj3isv5BaQrt0mdB7+3XWD+ASda33SY12\n", + "6wIDAQABo4GLMIGIMB8GA1UdIwQYMBaAFFcfgGtHzOeb+jWUfO2IuNEAWuCeMEIG\n", + "A1UdIAQ7MDkwNwYEVR0gADAvMC0GCCsGAQUFBwIBFiFodHRwOi8vd3d3LnN0LmNv\n", + "bS9UUE0vcmVwb3NpdG9yeS8wDAYDVR0TAQH/BAIwADATBgNVHSUBAf8ECTAHBgVn\n", + "gQUIATANBgkqhkiG9w0BAQsFAAOCAQEAMOhFPNcebyCRFOBztlWhmDb2DHTCD0nC\n", + "DVobH4WZJXGf4bkYNO3mOLyWtHEVzb36kiq7enh3f/eGhDPwKB8axlozpR5KAvER\n", + "szKNO8iLGOjuYzI2A4DazkttczFfzSB9QDgJrwTNEfIJtwRm2HQSiL0zzuEQOnaS\n", + "UWyt/iKn4/34BjEeaw4/Ld7+f06LXqSr18SUr0LTB2kk+Zzf0Och1C+G1CNLgJMM\n", + "MNQikAv0xdaOMX3HzA+phFlLbw/x8sboMlzmrbr92a/4Fp5WvmOSHH3ciwTtbAQn\n", + "A2TfExNOaKD2BG5FnB7c66puw2/yVxhveocQYgmT9XtMrNX00vEZJQ==\n", + "-----END CERTIFICATE-----\n", + NULL +}; + +/* + * Verify that a SubjectPublicKeyInfo with an id-RSAES-OAEP + * AlgorithmIdentifier decodes to an RSA EVP_PKEY via both the + * provider decoder path (exercised by X509_from_strings() + + * X509_get0_pubkey()) and the legacy type-specific path + * (exercised by d2i_RSA_PUBKEY() when available). + */ +static int test_rsaesoaep_spki(void) +{ + int ret = 0; + X509 *cert = NULL; + EVP_PKEY *pkey = NULL; +#ifndef OPENSSL_NO_DEPRECATED_3_0 + const X509_PUBKEY *xpk = NULL; + unsigned char *spki_der = NULL, *q; + const unsigned char *p; + int spki_len; + RSA *rsa = NULL; +#endif + + /* Provider / OSSL_DECODER path. */ + if (!TEST_ptr(cert = X509_from_strings(kRsaesOaepCert)) + || !TEST_ptr(pkey = X509_get0_pubkey(cert)) + || !TEST_int_eq(EVP_PKEY_get_base_id(pkey), EVP_PKEY_RSA) + || !TEST_int_ge(EVP_PKEY_get_bits(pkey), 2048)) + goto err; + +#ifndef OPENSSL_NO_DEPRECATED_3_0 + /* + * Legacy path: d2i_RSA_PUBKEY() routes through + * ossl_d2i_PUBKEY_legacy() which sets flag_force_legacy=1, + * so this exercises the NID_rsaesOaep -> NID_rsaEncryption + * remap in x509_pubkey_decode(). + */ + if (!TEST_ptr(xpk = X509_get_X509_PUBKEY(cert)) + || !TEST_int_gt((spki_len = i2d_X509_PUBKEY(xpk, NULL)), 0) + || !TEST_ptr(spki_der = OPENSSL_malloc(spki_len))) + goto err; + q = spki_der; + if (!TEST_int_eq(i2d_X509_PUBKEY(xpk, &q), spki_len)) + goto err; + p = spki_der; + if (!TEST_ptr(rsa = d2i_RSA_PUBKEY(NULL, &p, spki_len)) + || !TEST_int_ge(RSA_bits(rsa), 2048)) + goto err; +#endif + + ret = 1; +err: +#ifndef OPENSSL_NO_DEPRECATED_3_0 + RSA_free(rsa); + OPENSSL_free(spki_der); +#endif + X509_free(cert); + return ret; +} + OPT_TEST_DECLARE_USAGE("\n") int setup_tests(void) @@ -484,6 +574,7 @@ int setup_tests(void) ADD_TEST(test_x509_revoked_delete_last_extension); ADD_TEST(test_drop_empty_cert_keyids); ADD_TEST(test_drop_empty_csr_keyids); + ADD_TEST(test_rsaesoaep_spki); return 1; }