Commit cedff47f38 for openssl.org

commit cedff47f3895f17979988f4b5e93d0482ae5931c
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date:   Tue Jun 30 00:29:23 2026 +0200

    test: build the fake cipher provider as a loadable module

    The fake cipher provider was only available in-process, linked into test
    binaries via fake_cipher_start().  To exercise app success paths (e.g.
    skeyutl -genkey) the openssl app needs to load it as a provider module the
    same way it loads legacy.

    Make test/fake_cipherprov.c dual-buildable: drop the testutil dependency so
    the source links cleanly into a module, add an OSSL_provider_init entry point
    under FAKE_CIPHER_AS_MODULE, and add a fake-cipher MODULES target in
    test/build.info.  Also implement skeymgmt generate so opaque key generation
    works, and cover the skeyutl -genkey success path in 20-test_skeyutl.t.

    Assisted-by: Claude:claude-opus-4-8

    Reviewed-by: Matt Caswell <matt@openssl.foundation>
    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    MergeDate: Mon Jul 13 14:58:53 2026
    (Merged from https://github.com/openssl/openssl/pull/31781)

diff --git a/test/build.info b/test/build.info
index 86174be7c5..94e6d08865 100644
--- a/test/build.info
+++ b/test/build.info
@@ -1265,6 +1265,16 @@ IF[{- !$disabled{tests} -}]
       SOURCE[p_minimal]=p_minimal.ld
       GENERATE[p_minimal.ld]=../util/providers.num
     ENDIF
+    # Loadable form of the fake cipher provider.
+    MODULES{noinst}=fake-cipher
+    SOURCE[fake-cipher]=fake_cipherprov.c
+    DEFINE[fake-cipher]=FAKE_CIPHER_AS_MODULE
+    INCLUDE[fake-cipher]=../include
+    DEPEND[fake-cipher]=../libcrypto
+    IF[{- defined $target{shared_defflag} -}]
+      SOURCE[fake-cipher]=fake-cipher.ld
+      GENERATE[fake-cipher.ld]=../util/providers.num
+    ENDIF
   ENDIF
   IF[{- $disabled{module} || !$target{dso_scheme} -}]
     DEFINE[provider_test]=NO_PROVIDER_MODULE
diff --git a/test/fake_cipherprov.c b/test/fake_cipherprov.c
index 16fde61221..ad28a6b94b 100644
--- a/test/fake_cipherprov.c
+++ b/test/fake_cipherprov.c
@@ -9,13 +9,13 @@
  */

 #include <string.h>
+#include <openssl/err.h>
 #include <openssl/core_names.h>
 #include <openssl/core_object.h>
 #include <openssl/rand.h>
 #include <openssl/provider.h>
 #include <openssl/proverr.h>
 #include <openssl/param_build.h>
-#include "testutil.h"
 #include "fake_cipherprov.h"

 #define MAX_KEYNAME 32
@@ -65,7 +65,7 @@ static void *fake_skeymgmt_import(void *provctx, int selection, const OSSL_PARAM
 {
     PROV_CIPHER_FAKE_CTX *ctx = NULL;

-    if (!TEST_ptr(ctx = OPENSSL_zalloc(sizeof(PROV_CIPHER_FAKE_CTX))))
+    if ((ctx = OPENSSL_zalloc(sizeof(PROV_CIPHER_FAKE_CTX))) == NULL)
         return 0;

     if (ctx_from_key_params(ctx, p) != 1) {
@@ -76,6 +76,26 @@ static void *fake_skeymgmt_import(void *provctx, int selection, const OSSL_PARAM
     return ctx;
 }

+static void *fake_skeymgmt_generate(void *provctx, const OSSL_PARAM *params)
+{
+    PROV_CIPHER_FAKE_CTX *ctx = NULL;
+    size_t i;
+
+    if ((ctx = OPENSSL_zalloc(sizeof(PROV_CIPHER_FAKE_CTX))) == NULL)
+        return NULL;
+
+    if (ctx_from_key_params(ctx, params) != 1) {
+        OPENSSL_free(ctx);
+        return NULL;
+    }
+
+    /* Deterministic fill so the provider doesn't depend on a DRBG. */
+    for (i = 0; i < sizeof(ctx->key); i++)
+        ctx->key[i] = (unsigned char)i;
+
+    return ctx;
+}
+
 static int fake_skeymgmt_export(void *keydata, int selection,
     OSSL_CALLBACK *param_callback, void *cbarg)
 {
@@ -103,6 +123,7 @@ static int fake_skeymgmt_export(void *keydata, int selection,

 static const OSSL_DISPATCH fake_skeymgmt_funcs[] = {
     { OSSL_FUNC_SKEYMGMT_FREE, (void (*)(void))fake_skeymgmt_free },
+    { OSSL_FUNC_SKEYMGMT_GENERATE, (void (*)(void))fake_skeymgmt_generate },
     { OSSL_FUNC_SKEYMGMT_IMPORT, (void (*)(void))fake_skeymgmt_import },
     { OSSL_FUNC_SKEYMGMT_EXPORT, (void (*)(void))fake_skeymgmt_export },
     OSSL_DISPATCH_END
@@ -308,19 +329,29 @@ static int fake_cipher_provider_init(const OSSL_CORE_HANDLE *handle,
     const OSSL_DISPATCH *in,
     const OSSL_DISPATCH **out, void **provctx)
 {
-    if (!TEST_ptr(*provctx = OSSL_LIB_CTX_new()))
+    if ((*provctx = OSSL_LIB_CTX_new()) == NULL)
         return 0;
     *out = fake_cipher_method;
     return 1;
 }

+#ifdef FAKE_CIPHER_AS_MODULE
+/* Entry point when built as a loadable module (e.g. -provider fake-cipher). */
+int OSSL_provider_init(const OSSL_CORE_HANDLE *handle,
+    const OSSL_DISPATCH *in,
+    const OSSL_DISPATCH **out, void **provctx)
+{
+    return fake_cipher_provider_init(handle, in, out, provctx);
+}
+#endif
+
 OSSL_PROVIDER *fake_cipher_start(OSSL_LIB_CTX *libctx)
 {
     OSSL_PROVIDER *p;

-    if (!TEST_true(OSSL_PROVIDER_add_builtin(libctx, FAKE_PROV_NAME,
-            fake_cipher_provider_init))
-        || !TEST_ptr(p = OSSL_PROVIDER_try_load(libctx, FAKE_PROV_NAME, 1)))
+    if (!OSSL_PROVIDER_add_builtin(libctx, FAKE_PROV_NAME,
+            fake_cipher_provider_init)
+        || (p = OSSL_PROVIDER_try_load(libctx, FAKE_PROV_NAME, 1)) == NULL)
         return NULL;

     return p;
diff --git a/test/recipes/20-test_skeyutl.t b/test/recipes/20-test_skeyutl.t
index e173ba1d13..1c69935cac 100644
--- a/test/recipes/20-test_skeyutl.t
+++ b/test/recipes/20-test_skeyutl.t
@@ -9,12 +9,16 @@
 use strict;
 use warnings;

-use OpenSSL::Test qw/:DEFAULT with/;
+use OpenSSL::Test qw/:DEFAULT bldtop_dir with/;
 use OpenSSL::Test::Utils;

 setup("test_skeyutl");

-plan tests => 14;
+# The success path needs the loadable fake-cipher provider, which is only built
+# when module support is enabled.
+my $fake_cipher = !disabled('module');
+
+plan tests => 14 + ($fake_cipher ? 2 : 0);

 # Helper: run skeyutl expecting a non-zero (failure) exit code, and optionally
 # check that stderr matches a regular expression.
@@ -78,3 +82,18 @@ skeyutl_fails("skeyutl with an unknown cipher fails",
 skeyutl_fails("skeyutl with an unknown option fails",
               qr/Unknown option/,
               '-not-an-option');
+
+# Success path: load the fake-cipher provider, which implements opaque key
+# generation, and generate a key with it.
+if ($fake_cipher) {
+    $ENV{OPENSSL_MODULES} = bldtop_dir("test");
+    my @prov = ('-provider-path', bldtop_dir("test"), '-provider', 'fake-cipher');
+
+    my $status;
+    my @out = run(app(['openssl', 'skeyutl', @prov,
+                       '-genkey', '-skeymgmt', 'fake_cipher']),
+                  capture => 1, statusvar => \$status);
+    ok($status, "skeyutl -genkey with fake-cipher provider succeeds");
+    ok(grep(/opaque key/, @out),
+       "skeyutl -genkey reports the generated opaque key");
+}