Commit 90bf314b0b for openssl.org

commit 90bf314b0b5cbd9705e3b1862f633c2679c19e78
Author: Dmitry Belyavskiy <beldmit@gmail.com>
Date:   Mon Sep 21 16:50:08 2026 +0200

    Provide metadata for symmetric keys when parsing pkcs#12 files

    Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
    Reviewed-by: Simo Sorce <simo@redhat.com>
    MergeDate: Thu Sep 24 07:34:27 2026
    (Merged from https://github.com/openssl/openssl/pull/30937)

diff --git a/crypto/pkcs12/p12_kiss.c b/crypto/pkcs12/p12_kiss.c
index 55f2ca2b60..5b7ee063c2 100644
--- a/crypto/pkcs12/p12_kiss.c
+++ b/crypto/pkcs12/p12_kiss.c
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include "internal/cryptlib.h"
 #include <openssl/pkcs12.h>
+#include <openssl/core_names.h>
 #include "crypto/pkcs7/pk7_local.h"
 #include "p12_local.h"
 #include "crypto/x509.h" /* for ossl_x509_add_cert_new() */
@@ -368,12 +369,33 @@ static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen,
             return 1;
         {
             EVP_SKEY *skey;
+            OSSL_PARAM extra[3];
+            int nparams = 0;
+            unsigned char *fname_utf8 = NULL;
+            int fname_utf8_len = 0;
             PKCS8_PRIV_KEY_INFO *p8sb = PKCS12_decrypt_secretbag(bag, pass,
                 passlen, libctx, propq);
+
             if (p8sb == NULL)
                 return 0;
-            skey = PKCS8_PRIV_KEY_INFO_get1_skey(p8sb, libctx, propq);
+
+            if (fname) {
+                fname_utf8_len = ASN1_STRING_to_UTF8(&fname_utf8, fname);
+                if (fname_utf8_len >= 0)
+                    extra[nparams++] = OSSL_PARAM_construct_utf8_string(
+                        OSSL_SKEY_PARAM_ALIAS,
+                        (char *)fname_utf8, (size_t)fname_utf8_len);
+            }
+            if (lkid)
+                extra[nparams++] = OSSL_PARAM_construct_octet_string(
+                    OSSL_SKEY_PARAM_LOCAL_KEYID,
+                    lkid->data, (size_t)lkid->length);
+            extra[nparams] = OSSL_PARAM_construct_end();
+
+            skey = PKCS8_PRIV_KEY_INFO_get1_skey(p8sb, libctx, propq,
+                extra, 1);
             PKCS8_PRIV_KEY_INFO_free(p8sb);
+            OPENSSL_free(fname_utf8);
             if (skey == NULL) {
                 ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR);
                 return 0;
diff --git a/crypto/pkcs12/p12_sbag.c b/crypto/pkcs12/p12_sbag.c
index 40f53a71fa..860553162f 100644
--- a/crypto/pkcs12/p12_sbag.c
+++ b/crypto/pkcs12/p12_sbag.c
@@ -12,6 +12,7 @@
 #include <openssl/pkcs12.h>
 #include <openssl/core_names.h>
 #include <openssl/objects.h>
+#include <openssl/param_build.h>
 #include "p12_local.h"
 #include "crypto/x509.h"

@@ -150,17 +151,26 @@ X509_CRL *PKCS12_SAFEBAG_get1_crl_ex(const PKCS12_SAFEBAG *bag,
 }

 EVP_SKEY *PKCS8_PRIV_KEY_INFO_get1_skey(const PKCS8_PRIV_KEY_INFO *p8inf,
-    OSSL_LIB_CTX *libctx, const char *propq)
+    OSSL_LIB_CTX *libctx, const char *propq,
+    const OSSL_PARAM *extra_params, int strict)
 {
     const ASN1_OBJECT *algoid = NULL;
+    const X509_ALGOR *algor = NULL;
     const unsigned char *raw_key = NULL;
     int raw_key_len = 0;
     const char *skey_type = OSSL_SKEY_TYPE_GENERIC;
+    unsigned char *oid_der = NULL;
+    int oid_der_len = 0;
+    unsigned char *params_der = NULL;
+    int params_der_len = 0;
+    OSSL_PARAM_BLD *bld = NULL;
+    OSSL_PARAM *params = NULL;
+    EVP_SKEY *skey = NULL;

     if (p8inf == NULL)
         return NULL;

-    if (!PKCS8_pkey_get0(&algoid, &raw_key, &raw_key_len, NULL, p8inf)
+    if (!PKCS8_pkey_get0(&algoid, &raw_key, &raw_key_len, &algor, p8inf)
         || raw_key == NULL || raw_key_len <= 0)
         return NULL;

@@ -198,12 +208,73 @@ EVP_SKEY *PKCS8_PRIV_KEY_INFO_get1_skey(const PKCS8_PRIV_KEY_INFO *p8inf,
             skey_type = OSSL_SKEY_TYPE_AES;
             break;
         default:
+            if (strict != 0)
+                return NULL;
             break;
         }
     }

-    return EVP_SKEY_import_raw_key(libctx, skey_type,
-        (unsigned char *)raw_key, raw_key_len, propq);
+    bld = OSSL_PARAM_BLD_new();
+    if (bld == NULL)
+        return NULL;
+
+    if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_SKEY_PARAM_RAW_BYTES,
+            raw_key, (size_t)raw_key_len))
+        goto err;
+
+    if (algoid != NULL) {
+        oid_der_len = i2d_ASN1_OBJECT(algoid, &oid_der);
+        if (oid_der_len > 0
+            && !OSSL_PARAM_BLD_push_octet_string(bld,
+                OSSL_SKEY_PARAM_ALGORITHM_OID,
+                oid_der, (size_t)oid_der_len))
+            goto err;
+    }
+
+    if (algor != NULL && algor->parameter != NULL
+        && algor->parameter->type != V_ASN1_UNDEF) {
+        params_der_len = i2d_ASN1_TYPE(algor->parameter, &params_der);
+        if (params_der_len > 0
+            && !OSSL_PARAM_BLD_push_octet_string(bld,
+                OSSL_SKEY_PARAM_ALGORITHM_PARAMS,
+                params_der, (size_t)params_der_len))
+            goto err;
+    }
+
+    if (extra_params != NULL) {
+        const OSSL_PARAM *p;
+
+        for (p = extra_params; p->key != NULL; p++) {
+            switch (p->data_type) {
+            case OSSL_PARAM_UTF8_STRING:
+                if (!OSSL_PARAM_BLD_push_utf8_string(bld,
+                        p->key, p->data, p->data_size))
+                    goto err;
+                break;
+            case OSSL_PARAM_OCTET_STRING:
+                if (!OSSL_PARAM_BLD_push_octet_string(bld,
+                        p->key, p->data, p->data_size))
+                    goto err;
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+    params = OSSL_PARAM_BLD_to_param(bld);
+    if (params == NULL)
+        goto err;
+
+    skey = EVP_SKEY_import(libctx, skey_type, propq,
+        OSSL_SKEYMGMT_SELECT_ALL, params);
+
+err:
+    OSSL_PARAM_BLD_free(bld);
+    OSSL_PARAM_clear_free(params);
+    OPENSSL_free(oid_der);
+    OPENSSL_free(params_der);
+    return skey;
 }

 PKCS12_SAFEBAG *PKCS12_SAFEBAG_create_cert(X509 *x509)
diff --git a/doc/designs/evp_skey_metadata.md b/doc/designs/evp_skey_metadata.md
index fa8e09470b..59479a15c1 100644
--- a/doc/designs/evp_skey_metadata.md
+++ b/doc/designs/evp_skey_metadata.md
@@ -131,3 +131,79 @@ The implementation is in general provider-dependent. If the corresponding
 callback is not implemented for the EVP_SKEYMGMT, 0 is returned. However, as
 metadata is completely optional, the success doesn't indicate that the data is
 available.
+
+PKCS8_PRIV_KEY_INFO_get1_skey changes
+--------------------------------------
+
+The function gains two new parameters:
+
+```c
+EVP_SKEY *PKCS8_PRIV_KEY_INFO_get1_skey(const PKCS8_PRIV_KEY_INFO *p8inf,
+                                          OSSL_LIB_CTX *libctx,
+                                          const char *propq,
+                                          const OSSL_PARAM *extra_params,
+                                          int strict);
+```
+
+- **`extra_params`**: caller-built `OSSL_PARAM` array containing metadata
+  from bag attributes (local key ID and friendly name). May be NULL.
+  The function merges these with the params it builds from the PKCS8
+  structure (raw key bytes, algorithm identifier, algorithm parameters).
+
+- **`strict`**: controls how unrecognized algorithm OIDs are handled.
+  The function always attempts to match an algorithm-specific SKEYMGMT
+  based on the AlgorithmIdentifier OID (e.g. `OSSL_SKEY_TYPE_AES` for
+  AES NIDs).
+  - `0` (permissive, default): unrecognized OIDs fall back to
+    `OSSL_SKEY_TYPE_GENERIC`.
+  - non-zero (strict): unrecognized OIDs cause the function to return
+    NULL.
+
+The function now also:
+1. Extracts the full `X509_ALGOR` from the PKCS8 structure (via `PKCS8_pkey_get0`).
+2. DER-encodes the algorithm OID and adds it as
+   `OSSL_SKEY_PARAM_ALGORITHM_OID`.
+3. DER-encodes the algorithm parameters (if present) and adds them as
+   `OSSL_SKEY_PARAM_ALGORITHM_PARAMS`.
+4. Merges with `extra_params` (lkid, fname from caller).
+5. Calls `EVP_SKEY_import()` with the combined parameter set.
+
+Caller changes in p12_kiss.c
+-----------------------------
+
+In the `NID_secretBag` case in `parse_bag()`, the caller builds an
+`OSSL_PARAM` array with the friendly name and local key ID extracted
+from bag attributes, and passes it to `PKCS8_PRIV_KEY_INFO_get1_skey`
+via `extra_params`:
+
+```c
+case NID_secretBag:
+{
+    OSSL_PARAM extra[3];
+    int nparams = 0;
+    unsigned char *fname_utf8 = NULL;
+    int fname_utf8_len = 0;
+
+    if (fname) {
+        fname_utf8_len = ASN1_STRING_to_UTF8(&fname_utf8, fname);
+        if (fname_utf8_len >= 0) {
+            extra[nparams++] = OSSL_PARAM_construct_utf8_string(
+                OSSL_SKEY_PARAM_ALIAS,
+                (char *)fname_utf8, fname_utf8_len);
+        }
+    }
+    if (lkid) {
+        extra[nparams++] = OSSL_PARAM_construct_octet_string(
+            OSSL_SKEY_PARAM_LOCAL_KEYID,
+            lkid->data, lkid->length);
+    }
+    extra[nparams] = OSSL_PARAM_construct_end();
+
+    skey = PKCS8_PRIV_KEY_INFO_get1_skey(p8, ctx, propq, extra, 0);
+    OPENSSL_free(fname_utf8);
+    if (skey == NULL)
+        goto err;
+
+    /* push skey to stack ... */
+}
+```
diff --git a/doc/man3/PKCS12_SAFEBAG_get1_cert.pod b/doc/man3/PKCS12_SAFEBAG_get1_cert.pod
index 97ea4a51ae..d9a8b18557 100644
--- a/doc/man3/PKCS12_SAFEBAG_get1_cert.pod
+++ b/doc/man3/PKCS12_SAFEBAG_get1_cert.pod
@@ -33,7 +33,8 @@ PKCS8_PRIV_KEY_INFO_get1_skey - Get objects from a PKCS#12 safeBag
  const X509_SIG *PKCS12_SAFEBAG_get0_pkcs8(const PKCS12_SAFEBAG *bag);

  EVP_SKEY *PKCS8_PRIV_KEY_INFO_get1_skey(const PKCS8_PRIV_KEY_INFO *p8inf,
-                                    OSSL_LIB_CTX *libctx, const char *propq);
+                                    OSSL_LIB_CTX *libctx, const char *propq,
+                                    const OSSL_PARAM *extra_params, int strict);

 =head1 DESCRIPTION

@@ -66,11 +67,21 @@ safeContentsBag.

 PKCS8_PRIV_KEY_INFO_get1_skey() converts a decrypted B<PKCS8_PRIV_KEY_INFO> (as
 returned by L<PKCS12_decrypt_secretbag(3)>) into an B<EVP_SKEY> symmetric key
-object if it holds a symmetric key inside. The algorithm OID in I<p8inf> is
-used to determine the key type (e.g., AES). If the algorithm OID is not
-recognized by current implementation as matching the known key type, a generic
-symmetric key type will be used. The caller can use L<EVP_SKEY_get0_algorithm(3)>
-to retrieve the original AlgorithmIdentifier.
+object if it holds a symmetric key inside. The algorithm OID and parameters
+from the PKCS8 AlgorithmIdentifier are stored as metadata on the key and can be
+retrieved via L<EVP_SKEY_get0_algorithm_id(3)>.
+
+The I<strict> parameter controls how unrecognized algorithm OIDs are handled.
+In permissive mode (I<strict> is 0), recognized OIDs select an algorithm-specific
+SKEYMGMT (e.g., B<OSSL_SKEY_TYPE_AES> for AES NIDs) and unrecognized OIDs
+fall back to a generic key type.
+In strict mode (I<strict> is nonzero), only recognized OIDs are accepted and
+unrecognized OIDs cause the function to return NULL.
+
+I<extra_params> is an optional caller-built L<OSSL_PARAM(3)> array containing
+additional metadata to attach to the key (e.g., B<OSSL_SKEY_PARAM_ALIAS> for the
+friendly name and B<OSSL_SKEY_PARAM_LOCAL_KEYID> for the local key ID from
+PKCS#12 bag attributes). May be NULL.

 =head1 RETURN VALUES

@@ -84,7 +95,7 @@ L<PKCS12_create(3)>,
 L<PKCS12_add_safe(3)>,
 L<PKCS12_add_safes(3)>,
 L<PKCS12_decrypt_secretbag(3)>,
-L<EVP_SKEY_get0_algorithm(3)>
+L<EVP_SKEY_get0_algorithm_id(3)>

 =head1 HISTORY

diff --git a/include/openssl/pkcs12.h.in b/include/openssl/pkcs12.h.in
index e966228ea9..1c470f15e0 100644
--- a/include/openssl/pkcs12.h.in
+++ b/include/openssl/pkcs12.h.in
@@ -127,7 +127,8 @@ X509 *PKCS12_SAFEBAG_get1_cert(const PKCS12_SAFEBAG *bag);
 X509_CRL *PKCS12_SAFEBAG_get1_crl_ex(const PKCS12_SAFEBAG *bag, OSSL_LIB_CTX *libctx, const char *propq);
 X509_CRL *PKCS12_SAFEBAG_get1_crl(const PKCS12_SAFEBAG *bag);
 EVP_SKEY *PKCS8_PRIV_KEY_INFO_get1_skey(const PKCS8_PRIV_KEY_INFO *p8inf,
-    OSSL_LIB_CTX *libctx, const char *propq);
+    OSSL_LIB_CTX *libctx, const char *propq,
+    const OSSL_PARAM *extra_params, int strict);
 const STACK_OF(PKCS12_SAFEBAG) *
 PKCS12_SAFEBAG_get0_safes(const PKCS12_SAFEBAG *bag);
 const PKCS8_PRIV_KEY_INFO *PKCS12_SAFEBAG_get0_p8inf(const PKCS12_SAFEBAG *bag);