When using PEM_read_bio_PrivateKey_ex() the public key is optional
authorTomas Mraz <tomas@openssl.org>
Wed, 26 Oct 2022 09:29:49 +0000 (11:29 +0200)
committerTomas Mraz <tomas@openssl.org>
Fri, 25 Nov 2022 09:32:18 +0000 (10:32 +0100)
Fixes #19498

However the private key part is not optional which was
mishandled by the legacy routine.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19505)

crypto/pem/pem_pkey.c

index 8ffeed9d788d9455423abad536f3e805ae92c580..3e76852c67a44a69ad74e3366a48012dda867a6b 100644 (file)
@@ -75,6 +75,10 @@ static EVP_PKEY *pem_read_bio_key_decoder(BIO *bp, EVP_PKEY **x,
         }
     ERR_pop_to_mark();
 
+    /* if we were asked for private key, the public key is optional */
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        selection = selection & ~OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
+
     if (!evp_keymgmt_util_has(pkey, selection)) {
         EVP_PKEY_free(pkey);
         pkey = NULL;
@@ -106,7 +110,7 @@ static EVP_PKEY *pem_read_bio_key_legacy(BIO *bp, EVP_PKEY **x,
     EVP_PKEY *ret = NULL;
 
     ERR_set_mark();  /* not interested in PEM read errors */
-    if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) {
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
         if (!PEM_bytes_read_bio_secmem(&data, &len, &nm,
                                        PEM_STRING_EVP_PKEY,
                                        bp, cb, u)) {
@@ -116,7 +120,7 @@ static EVP_PKEY *pem_read_bio_key_legacy(BIO *bp, EVP_PKEY **x,
     } else {
         const char *pem_string = PEM_STRING_PARAMETERS;
 
-        if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
             pem_string = PEM_STRING_PUBLIC;
         if (!PEM_bytes_read_bio(&data, &len, &nm,
                                 pem_string,
@@ -174,9 +178,13 @@ static EVP_PKEY *pem_read_bio_key_legacy(BIO *bp, EVP_PKEY **x,
             goto p8err;
         ret = ossl_d2i_PrivateKey_legacy(ameth->pkey_id, x, &p, len, libctx,
                                          propq);
-    } else if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) {
+    } else if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0
+               && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        /* Trying legacy PUBKEY decoding only if we do not want private key. */
         ret = ossl_d2i_PUBKEY_legacy(x, &p, len);
-    } else if ((slen = ossl_pem_check_suffix(nm, "PARAMETERS")) > 0) {
+    } else if ((selection & EVP_PKEY_KEYPAIR) == 0
+               && (slen = ossl_pem_check_suffix(nm, "PARAMETERS")) > 0) {
+        /* Trying legacy params decoding only if we do not want a key. */
         ret = EVP_PKEY_new();
         if (ret == NULL)
             goto err;
@@ -294,6 +302,7 @@ EVP_PKEY *PEM_read_bio_PrivateKey_ex(BIO *bp, EVP_PKEY **x,
                                      OSSL_LIB_CTX *libctx, const char *propq)
 {
     return pem_read_bio_key(bp, x, cb, u, libctx, propq,
+                            /* we also want the public key, if available */
                             EVP_PKEY_KEYPAIR);
 }