/*
 * Copyright 2021-2025 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
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#include <stddef.h>
#include <string.h>
#include <openssl/provider.h>
#include <openssl/params.h>
#include <openssl/core_names.h>
#include <openssl/evp.h>
#include <openssl/store.h>
#include <openssl/ui.h>
#include "testutil.h"
#include "fake_rsaprov.h"

static OSSL_LIB_CTX *libctx = NULL;
extern int key_deleted; /* From fake_rsaprov.c */

/* Fetch SIGNATURE method using a libctx and propq */
static int fetch_sig(OSSL_LIB_CTX *ctx, const char *alg, const char *propq,
                     OSSL_PROVIDER *expected_prov)
{
    OSSL_PROVIDER *prov;
    EVP_SIGNATURE *sig = EVP_SIGNATURE_fetch(ctx, "RSA", propq);
    int ret = 0;

    if (!TEST_ptr(sig))
        return 0;

    if (!TEST_ptr(prov = EVP_SIGNATURE_get0_provider(sig)))
        goto end;

    if (!TEST_ptr_eq(prov, expected_prov)) {
        TEST_info("Fetched provider: %s, Expected provider: %s",
                  OSSL_PROVIDER_get0_name(prov),
                  OSSL_PROVIDER_get0_name(expected_prov));
        goto end;
    }

    ret = 1;
end:
    EVP_SIGNATURE_free(sig);
    return ret;
}


static int test_pkey_sig(void)
{
    OSSL_PROVIDER *deflt = NULL;
    OSSL_PROVIDER *fake_rsa = NULL;
    int i, ret = 0;
    EVP_PKEY *pkey = NULL;
    EVP_PKEY_CTX *ctx = NULL;

    if (!TEST_ptr(fake_rsa = fake_rsa_start(libctx)))
        return 0;

    if (!TEST_ptr(deflt = OSSL_PROVIDER_load(libctx, "default")))
        goto end;

    /* Do a direct fetch to see it works */
    if (!TEST_true(fetch_sig(libctx, "RSA", "provider=fake-rsa", fake_rsa))
        || !TEST_true(fetch_sig(libctx, "RSA", "?provider=fake-rsa", fake_rsa)))
        goto end;

    /* Construct a pkey using precise propq to use our provider */
    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA",
                                                   "provider=fake-rsa"))
        || !TEST_true(EVP_PKEY_fromdata_init(ctx))
        || !TEST_true(EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, NULL))
        || !TEST_ptr(pkey))
        goto end;

    EVP_PKEY_CTX_free(ctx);
    ctx = NULL;

    /* try exercising signature_init ops a few times */
    for (i = 0; i < 3; i++) {
        size_t siglen;

        /*
         * Create a signing context for our pkey with optional propq.
         * The sign init should pick both keymgmt and signature from
         * fake-rsa as the key is not exportable.
         */
        if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey,
                                                       "?provider=default")))
            goto end;

        /*
         * If this picks the wrong signature without realizing it
         * we can get a segfault or some internal error. At least watch
         * whether fake-rsa sign_init is exercised by calling sign.
         */
        if (!TEST_int_eq(EVP_PKEY_sign_init(ctx), 1))
            goto end;

        if (!TEST_int_eq(EVP_PKEY_sign(ctx, NULL, &siglen, NULL, 0), 1)
            || !TEST_size_t_eq(siglen, 256))
            goto end;

        EVP_PKEY_CTX_free(ctx);
        ctx = NULL;
    }

    ret = 1;

end:
    fake_rsa_finish(fake_rsa);
    OSSL_PROVIDER_unload(deflt);
    EVP_PKEY_CTX_free(ctx);
    EVP_PKEY_free(pkey);
    return ret;
}

static int test_alternative_keygen_init(void)
{
    EVP_PKEY_CTX *ctx = NULL;
    OSSL_PROVIDER *deflt = NULL;
    OSSL_PROVIDER *fake_rsa = NULL;
    const OSSL_PROVIDER *provider;
    const char *provname;
    int ret = 0;

    if (!TEST_ptr(deflt = OSSL_PROVIDER_load(libctx, "default")))
        goto end;

    /* first try without the fake RSA provider loaded */
    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA", NULL)))
        goto end;

    if (!TEST_int_gt(EVP_PKEY_keygen_init(ctx), 0))
        goto end;

    if (!TEST_ptr(provider = EVP_PKEY_CTX_get0_provider(ctx)))
        goto end;

    if (!TEST_ptr(provname = OSSL_PROVIDER_get0_name(provider)))
        goto end;

    if (!TEST_str_eq(provname, "default"))
        goto end;

    EVP_PKEY_CTX_free(ctx);
    ctx = NULL;

    /* now load fake RSA and try again */
    if (!TEST_ptr(fake_rsa = fake_rsa_start(libctx)))
        return 0;

    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA",
                                                   "?provider=fake-rsa")))
        goto end;

    if (!TEST_int_gt(EVP_PKEY_keygen_init(ctx), 0))
        goto end;

    if (!TEST_ptr(provider = EVP_PKEY_CTX_get0_provider(ctx)))
        goto end;

    if (!TEST_ptr(provname = OSSL_PROVIDER_get0_name(provider)))
        goto end;

    if (!TEST_str_eq(provname, "fake-rsa"))
        goto end;

    ret = 1;

end:
    fake_rsa_finish(fake_rsa);
    OSSL_PROVIDER_unload(deflt);
    EVP_PKEY_CTX_free(ctx);
    return ret;
}

static int test_pkey_eq(void)
{
    OSSL_PROVIDER *deflt = NULL;
    OSSL_PROVIDER *fake_rsa = NULL;
    EVP_PKEY *pkey_fake = NULL;
    EVP_PKEY *pkey_dflt = NULL;
    EVP_PKEY_CTX *ctx = NULL;
    OSSL_PARAM *params = NULL;
    int ret = 0;

    if (!TEST_ptr(fake_rsa = fake_rsa_start(libctx)))
        return 0;

    if (!TEST_ptr(deflt = OSSL_PROVIDER_load(libctx, "default")))
        goto end;

    /* Construct a public key for fake-rsa */
    if (!TEST_ptr(params = fake_rsa_key_params(0))
        || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA",
                                                      "provider=fake-rsa"))
        || !TEST_true(EVP_PKEY_fromdata_init(ctx))
        || !TEST_true(EVP_PKEY_fromdata(ctx, &pkey_fake, EVP_PKEY_PUBLIC_KEY,
                                        params))
        || !TEST_ptr(pkey_fake))
        goto end;

    EVP_PKEY_CTX_free(ctx);
    ctx = NULL;
    OSSL_PARAM_free(params);
    params = NULL;

    /* Construct a public key for default */
    if (!TEST_ptr(params = fake_rsa_key_params(0))
        || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA",
                                                      "provider=default"))
        || !TEST_true(EVP_PKEY_fromdata_init(ctx))
        || !TEST_true(EVP_PKEY_fromdata(ctx, &pkey_dflt, EVP_PKEY_PUBLIC_KEY,
                                        params))
        || !TEST_ptr(pkey_dflt))
        goto end;

    EVP_PKEY_CTX_free(ctx);
    ctx = NULL;
    OSSL_PARAM_free(params);
    params = NULL;

    /* now test for equality */
    if (!TEST_int_eq(EVP_PKEY_eq(pkey_fake, pkey_dflt), 1))
        goto end;

    ret = 1;
end:
    fake_rsa_finish(fake_rsa);
    OSSL_PROVIDER_unload(deflt);
    EVP_PKEY_CTX_free(ctx);
    EVP_PKEY_free(pkey_fake);
    EVP_PKEY_free(pkey_dflt);
    OSSL_PARAM_free(params);
    return ret;
}

static int test_pkey_store(int idx)
{
    OSSL_PROVIDER *deflt = NULL;
    OSSL_PROVIDER *fake_rsa = NULL;
    int ret = 0;
    EVP_PKEY *pkey = NULL;
    OSSL_STORE_LOADER *loader = NULL;
    OSSL_STORE_CTX *ctx = NULL;
    OSSL_STORE_INFO *info;
    const char *propq = idx == 0 ? "?provider=fake-rsa"
                                 : "?provider=default";

    /* It's important to load the default provider first for this test */
    if (!TEST_ptr(deflt = OSSL_PROVIDER_load(libctx, "default")))
        goto end;

    if (!TEST_ptr(fake_rsa = fake_rsa_start(libctx)))
        goto end;

    if (!TEST_ptr(loader = OSSL_STORE_LOADER_fetch(libctx, "fake_rsa",
                                                   propq)))
        goto end;

    OSSL_STORE_LOADER_free(loader);

    if (!TEST_ptr(ctx = OSSL_STORE_open_ex("fake_rsa:test", libctx, propq,
                                           NULL, NULL, NULL, NULL, NULL)))
        goto end;

    while (!OSSL_STORE_eof(ctx)
           && (info = OSSL_STORE_load(ctx)) != NULL
           && pkey == NULL) {
        if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY)
            pkey = OSSL_STORE_INFO_get1_PKEY(info);
        OSSL_STORE_INFO_free(info);
        info = NULL;
    }

    if (!TEST_ptr(pkey) || !TEST_int_eq(EVP_PKEY_is_a(pkey, "RSA"), 1))
        goto end;

    ret = 1;

end:
    fake_rsa_finish(fake_rsa);
    OSSL_PROVIDER_unload(deflt);
    OSSL_STORE_close(ctx);
    EVP_PKEY_free(pkey);
    return ret;
}

static int test_pkey_delete(void)
{
    OSSL_PROVIDER *deflt = NULL;
    OSSL_PROVIDER *fake_rsa = NULL;
    int ret = 0;
    EVP_PKEY *pkey = NULL;
    OSSL_STORE_LOADER *loader = NULL;
    OSSL_STORE_CTX *ctx = NULL;
    OSSL_STORE_INFO *info;
    const char *propq = "?provider=fake-rsa";

    /* It's important to load the default provider first for this test */
    if (!TEST_ptr(deflt = OSSL_PROVIDER_load(libctx, "default")))
        goto end;

    if (!TEST_ptr(fake_rsa = fake_rsa_start(libctx)))
        goto end;

    if (!TEST_ptr(loader = OSSL_STORE_LOADER_fetch(libctx, "fake_rsa",
                                                   propq)))
        goto end;

    OSSL_STORE_LOADER_free(loader);

    /* First iteration: load key, check it, delete it */
    if (!TEST_ptr(ctx = OSSL_STORE_open_ex("fake_rsa:test", libctx, propq,
                                           NULL, NULL, NULL, NULL, NULL)))
        goto end;

    while (!OSSL_STORE_eof(ctx)
           && (info = OSSL_STORE_load(ctx)) != NULL
           && pkey == NULL) {
        if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY)
            pkey = OSSL_STORE_INFO_get1_PKEY(info);
        OSSL_STORE_INFO_free(info);
        info = NULL;
    }

    if (!TEST_ptr(pkey) || !TEST_int_eq(EVP_PKEY_is_a(pkey, "RSA"), 1))
        goto end;
    EVP_PKEY_free(pkey);
    pkey = NULL;

    if (!TEST_int_eq(OSSL_STORE_delete("fake_rsa:test", libctx, propq,
                                       NULL, NULL, NULL), 1))
        goto end;
    if (!TEST_int_eq(OSSL_STORE_close(ctx), 1))
        goto end;

    /* Second iteration: load key should fail */
    if (!TEST_ptr(ctx = OSSL_STORE_open_ex("fake_rsa:test", libctx, propq,
                                           NULL, NULL, NULL, NULL, NULL)))
        goto end;

    while (!OSSL_STORE_eof(ctx)) {
           info = OSSL_STORE_load(ctx);
	   if (!TEST_ptr_null(info))
               goto end;
    }

    ret = 1;

end:
    fake_rsa_finish(fake_rsa);
    OSSL_PROVIDER_unload(deflt);
    OSSL_STORE_close(ctx);
    fake_rsa_restore_store_state();
    return ret;
}

static int fake_pw_read_string(UI *ui, UI_STRING *uis)
{
    const char *passphrase = FAKE_PASSPHRASE;

    if (UI_get_string_type(uis) == UIT_PROMPT) {
        UI_set_result(ui, uis, passphrase);
        return 1;
    }

    return 0;
}

static int test_pkey_store_open_ex(void)
{
    OSSL_PROVIDER *deflt = NULL;
    OSSL_PROVIDER *fake_rsa = NULL;
    int ret = 0;
    EVP_PKEY *pkey = NULL;
    OSSL_STORE_LOADER *loader = NULL;
    OSSL_STORE_CTX *ctx = NULL;
    const char *propq = "?provider=fake-rsa";
    UI_METHOD *ui_method = NULL;

    /* It's important to load the default provider first for this test */
    if (!TEST_ptr(deflt = OSSL_PROVIDER_load(libctx, "default")))
        goto end;

    if (!TEST_ptr(fake_rsa = fake_rsa_start(libctx)))
        goto end;

    if (!TEST_ptr(loader = OSSL_STORE_LOADER_fetch(libctx, "fake_rsa",
                                                   propq)))
        goto end;

    OSSL_STORE_LOADER_free(loader);

    if (!TEST_ptr(ui_method= UI_create_method("PW Callbacks")))
        goto end;

    if (UI_method_set_reader(ui_method, fake_pw_read_string))
        goto end;

    if (!TEST_ptr(ctx = OSSL_STORE_open_ex("fake_rsa:openpwtest", libctx, propq,
                                           ui_method, NULL, NULL, NULL, NULL)))
        goto end;

    /* retry w/o ui_method to ensure we actually enter pw checks and fail */
    OSSL_STORE_close(ctx);
    if (!TEST_ptr_null(ctx = OSSL_STORE_open_ex("fake_rsa:openpwtest", libctx,
                                                propq, NULL, NULL, NULL, NULL,
                                                NULL)))
        goto end;

    ret = 1;

end:
    UI_destroy_method(ui_method);
    fake_rsa_finish(fake_rsa);
    OSSL_PROVIDER_unload(deflt);
    OSSL_STORE_close(ctx);
    EVP_PKEY_free(pkey);
    return ret;
}

#define DEFAULT_PROVIDER_IDX    0
#define FAKE_RSA_PROVIDER_IDX   1

static int reset_ctx_providers(OSSL_LIB_CTX **ctx, OSSL_PROVIDER *providers[2], const char *prop)
{
    OSSL_PROVIDER_unload(providers[DEFAULT_PROVIDER_IDX]);
    providers[DEFAULT_PROVIDER_IDX] = NULL;
    fake_rsa_finish(providers[FAKE_RSA_PROVIDER_IDX]);
    providers[FAKE_RSA_PROVIDER_IDX] = NULL;
    OSSL_LIB_CTX_free(*ctx);
    *ctx = NULL;

    if (!TEST_ptr(*ctx = OSSL_LIB_CTX_new())
        || !TEST_ptr(providers[DEFAULT_PROVIDER_IDX] = OSSL_PROVIDER_load(*ctx, "default"))
        || !TEST_ptr(providers[FAKE_RSA_PROVIDER_IDX] = fake_rsa_start(*ctx))
        || !TEST_true(EVP_set_default_properties(*ctx, prop)))
        return 0;
    return 1;
}

struct test_pkey_decoder_properties_t {
    const char *provider_props;
    const char *explicit_props;
    int curr_provider_idx;
};

static int test_pkey_provider_decoder_props(void)
{
    OSSL_LIB_CTX *my_libctx = NULL;
    OSSL_PROVIDER *providers[2] = { NULL };
    struct test_pkey_decoder_properties_t properties_test[] = {
        { "?provider=fake-rsa", NULL, FAKE_RSA_PROVIDER_IDX },
        { "?provider=default", NULL, DEFAULT_PROVIDER_IDX },
        { NULL, "?provider=fake-rsa", FAKE_RSA_PROVIDER_IDX },
        { NULL, "?provider=default", DEFAULT_PROVIDER_IDX },
        { NULL, "provider=fake-rsa", FAKE_RSA_PROVIDER_IDX },
        { NULL, "provider=default", DEFAULT_PROVIDER_IDX },
    };
    EVP_PKEY *pkey = NULL;
    BIO *bio_priv = NULL;
    unsigned char *encoded_pub = NULL;
    int len_pub;
    const unsigned char *p;
    PKCS8_PRIV_KEY_INFO *p8 = NULL;
    size_t i;
    int ret = 0;
    const char pem_rsa_priv_key[] = {
        0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50,
        0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D,
        0x2D, 0x2D, 0x2D, 0x0A, 0x4D, 0x49, 0x49, 0x45, 0x76, 0x51, 0x49, 0x42,
        0x41, 0x44, 0x41, 0x4E, 0x42, 0x67, 0x6B, 0x71, 0x68, 0x6B, 0x69, 0x47,
        0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43,
        0x42, 0x4B, 0x63, 0x77, 0x67, 0x67, 0x53, 0x6A, 0x41, 0x67, 0x45, 0x41,
        0x41, 0x6F, 0x49, 0x42, 0x41, 0x51, 0x44, 0x45, 0x6B, 0x43, 0x34, 0x5A,
        0x57, 0x76, 0x33, 0x75, 0x63, 0x46, 0x62, 0x55, 0x0A, 0x46, 0x38, 0x59,
        0x77, 0x6C, 0x55, 0x72, 0x6D, 0x51, 0x6C, 0x4C, 0x43, 0x5A, 0x77, 0x41,
        0x67, 0x72, 0x34, 0x44, 0x50, 0x55, 0x41, 0x46, 0x56, 0x48, 0x6C, 0x2B,
        0x77, 0x46, 0x63, 0x58, 0x79, 0x70, 0x56, 0x67, 0x53, 0x63, 0x56, 0x59,
        0x34, 0x4B, 0x37, 0x51, 0x6D, 0x64, 0x57, 0x4B, 0x73, 0x59, 0x71, 0x62,
        0x38, 0x74, 0x70, 0x4F, 0x78, 0x71, 0x77, 0x30, 0x4E, 0x77, 0x5A, 0x57,
        0x58, 0x0A, 0x4F, 0x2B, 0x74, 0x61, 0x34, 0x2B, 0x79, 0x32, 0x37, 0x43,
        0x4F, 0x75, 0x66, 0x6F, 0x4F, 0x68, 0x52, 0x54, 0x4D, 0x77, 0x4E, 0x79,
        0x4E, 0x32, 0x4C, 0x77, 0x53, 0x4E, 0x54, 0x50, 0x4E, 0x33, 0x65, 0x45,
        0x6B, 0x34, 0x65, 0x65, 0x35, 0x51, 0x6E, 0x70, 0x70, 0x45, 0x79, 0x44,
        0x72, 0x71, 0x6F, 0x43, 0x67, 0x76, 0x54, 0x6C, 0x41, 0x41, 0x64, 0x54,
        0x6F, 0x46, 0x61, 0x58, 0x76, 0x6A, 0x0A, 0x78, 0x31, 0x33, 0x59, 0x62,
        0x6A, 0x37, 0x6A, 0x66, 0x68, 0x77, 0x4E, 0x37, 0x34, 0x71, 0x4B, 0x64,
        0x71, 0x73, 0x53, 0x45, 0x74, 0x50, 0x57, 0x79, 0x67, 0x67, 0x65, 0x6F,
        0x74, 0x69, 0x51, 0x53, 0x50, 0x79, 0x36, 0x4B, 0x79, 0x42, 0x49, 0x75,
        0x57, 0x74, 0x49, 0x78, 0x50, 0x41, 0x41, 0x38, 0x6A, 0x41, 0x76, 0x66,
        0x41, 0x6E, 0x51, 0x6A, 0x31, 0x65, 0x58, 0x68, 0x67, 0x68, 0x46, 0x0A,
        0x4E, 0x32, 0x4E, 0x78, 0x6B, 0x71, 0x67, 0x78, 0x76, 0x42, 0x59, 0x64,
        0x4E, 0x79, 0x31, 0x6D, 0x33, 0x2B, 0x6A, 0x58, 0x41, 0x43, 0x50, 0x4C,
        0x52, 0x7A, 0x63, 0x31, 0x31, 0x5A, 0x62, 0x4E, 0x48, 0x4B, 0x69, 0x77,
        0x68, 0x43, 0x59, 0x31, 0x2F, 0x48, 0x69, 0x53, 0x42, 0x6B, 0x77, 0x48,
        0x6C, 0x49, 0x4B, 0x2B, 0x2F, 0x56, 0x4C, 0x6A, 0x32, 0x73, 0x6D, 0x43,
        0x4B, 0x64, 0x55, 0x51, 0x0A, 0x67, 0x76, 0x4C, 0x58, 0x53, 0x6E, 0x6E,
        0x56, 0x67, 0x51, 0x75, 0x6C, 0x48, 0x69, 0x6F, 0x44, 0x36, 0x55, 0x67,
        0x59, 0x38, 0x78, 0x41, 0x32, 0x61, 0x34, 0x4D, 0x31, 0x72, 0x68, 0x59,
        0x75, 0x54, 0x56, 0x38, 0x42, 0x72, 0x50, 0x52, 0x5A, 0x34, 0x42, 0x46,
        0x78, 0x32, 0x6F, 0x30, 0x6A, 0x59, 0x57, 0x76, 0x47, 0x62, 0x41, 0x2F,
        0x48, 0x6C, 0x70, 0x37, 0x66, 0x54, 0x4F, 0x79, 0x2B, 0x0A, 0x46, 0x35,
        0x4F, 0x6B, 0x69, 0x48, 0x53, 0x37, 0x41, 0x67, 0x4D, 0x42, 0x41, 0x41,
        0x45, 0x43, 0x67, 0x67, 0x45, 0x41, 0x59, 0x67, 0x43, 0x75, 0x38, 0x31,
        0x5A, 0x69, 0x51, 0x42, 0x56, 0x44, 0x76, 0x57, 0x69, 0x44, 0x47, 0x4B,
        0x72, 0x2B, 0x31, 0x70, 0x49, 0x66, 0x32, 0x43, 0x78, 0x70, 0x72, 0x47,
        0x4A, 0x45, 0x6D, 0x31, 0x68, 0x38, 0x36, 0x5A, 0x63, 0x45, 0x78, 0x33,
        0x4C, 0x37, 0x0A, 0x71, 0x46, 0x44, 0x57, 0x2B, 0x67, 0x38, 0x48, 0x47,
        0x57, 0x64, 0x30, 0x34, 0x53, 0x33, 0x71, 0x76, 0x68, 0x39, 0x4C, 0x75,
        0x62, 0x6C, 0x41, 0x4A, 0x7A, 0x65, 0x74, 0x41, 0x50, 0x78, 0x52, 0x58,
        0x4C, 0x39, 0x7A, 0x78, 0x33, 0x50, 0x58, 0x6A, 0x4A, 0x5A, 0x73, 0x37,
        0x65, 0x33, 0x48, 0x4C, 0x45, 0x75, 0x6E, 0x79, 0x33, 0x54, 0x61, 0x57,
        0x65, 0x7A, 0x30, 0x58, 0x49, 0x30, 0x4F, 0x0A, 0x34, 0x4C, 0x53, 0x59,
        0x38, 0x53, 0x38, 0x64, 0x36, 0x70, 0x56, 0x42, 0x50, 0x6D, 0x55, 0x45,
        0x74, 0x77, 0x47, 0x57, 0x4E, 0x34, 0x76, 0x59, 0x71, 0x48, 0x6E, 0x4B,
        0x4C, 0x58, 0x4F, 0x62, 0x34, 0x51, 0x51, 0x41, 0x58, 0x73, 0x34, 0x4D,
        0x7A, 0x66, 0x6B, 0x4D, 0x2F, 0x4D, 0x65, 0x2F, 0x62, 0x2B, 0x7A, 0x64,
        0x75, 0x31, 0x75, 0x6D, 0x77, 0x6A, 0x4D, 0x6C, 0x33, 0x44, 0x75, 0x64,
        0x0A, 0x35, 0x72, 0x56, 0x68, 0x6B, 0x67, 0x76, 0x74, 0x38, 0x75, 0x68,
        0x44, 0x55, 0x47, 0x33, 0x58, 0x53, 0x48, 0x65, 0x6F, 0x4A, 0x59, 0x42,
        0x4D, 0x62, 0x54, 0x39, 0x69, 0x6B, 0x4A, 0x44, 0x56, 0x4D, 0x4A, 0x35,
        0x31, 0x72, 0x72, 0x65, 0x2F, 0x31, 0x52, 0x69, 0x64, 0x64, 0x67, 0x78,
        0x70, 0x38, 0x53, 0x6B, 0x74, 0x56, 0x6B, 0x76, 0x47, 0x6D, 0x4D, 0x6C,
        0x39, 0x6B, 0x51, 0x52, 0x38, 0x0A, 0x38, 0x64, 0x76, 0x33, 0x50, 0x78,
        0x2F, 0x6B, 0x54, 0x4E, 0x39, 0x34, 0x45, 0x75, 0x52, 0x67, 0x30, 0x43,
        0x6B, 0x58, 0x42, 0x68, 0x48, 0x70, 0x6F, 0x47, 0x6F, 0x34, 0x71, 0x6E,
        0x4D, 0x33, 0x51, 0x33, 0x42, 0x35, 0x50, 0x6C, 0x6D, 0x53, 0x4B, 0x35,
        0x67, 0x6B, 0x75, 0x50, 0x76, 0x57, 0x79, 0x39, 0x6C, 0x38, 0x4C, 0x2F,
        0x54, 0x56, 0x74, 0x38, 0x4C, 0x62, 0x36, 0x2F, 0x7A, 0x4C, 0x0A, 0x42,
        0x79, 0x51, 0x57, 0x2B, 0x67, 0x30, 0x32, 0x77, 0x78, 0x65, 0x4E, 0x47,
        0x68, 0x77, 0x31, 0x66, 0x6B, 0x44, 0x2B, 0x58, 0x46, 0x48, 0x37, 0x4B,
        0x6B, 0x53, 0x65, 0x57, 0x6C, 0x2B, 0x51, 0x6E, 0x72, 0x4C, 0x63, 0x65,
        0x50, 0x4D, 0x30, 0x68, 0x51, 0x4B, 0x42, 0x67, 0x51, 0x44, 0x78, 0x6F,
        0x71, 0x55, 0x6B, 0x30, 0x50, 0x4C, 0x4F, 0x59, 0x35, 0x57, 0x67, 0x4F,
        0x6B, 0x67, 0x72, 0x0A, 0x75, 0x6D, 0x67, 0x69, 0x65, 0x2F, 0x4B, 0x31,
        0x57, 0x4B, 0x73, 0x2B, 0x69, 0x7A, 0x54, 0x74, 0x41, 0x70, 0x6A, 0x7A,
        0x63, 0x4D, 0x37, 0x36, 0x73, 0x7A, 0x61, 0x36, 0x33, 0x62, 0x35, 0x52,
        0x39, 0x77, 0x2B, 0x50, 0x2B, 0x4E, 0x73, 0x73, 0x4D, 0x56, 0x34, 0x61,
        0x65, 0x56, 0x39, 0x65, 0x70, 0x45, 0x47, 0x5A, 0x4F, 0x36, 0x38, 0x49,
        0x55, 0x6D, 0x69, 0x30, 0x51, 0x6A, 0x76, 0x51, 0x0A, 0x6E, 0x70, 0x6C,
        0x75, 0x51, 0x6F, 0x61, 0x64, 0x46, 0x59, 0x77, 0x65, 0x46, 0x77, 0x53,
        0x51, 0x31, 0x31, 0x42, 0x58, 0x48, 0x6F, 0x65, 0x51, 0x42, 0x41, 0x34,
        0x6E, 0x4E, 0x70, 0x6B, 0x72, 0x56, 0x35, 0x38, 0x68, 0x67, 0x7A, 0x5A,
        0x4E, 0x33, 0x6D, 0x39, 0x4A, 0x4C, 0x52, 0x37, 0x4A, 0x78, 0x79, 0x72,
        0x49, 0x71, 0x58, 0x73, 0x52, 0x6E, 0x55, 0x7A, 0x6C, 0x31, 0x33, 0x4B,
        0x6A, 0x0A, 0x47, 0x7A, 0x5A, 0x42, 0x43, 0x4A, 0x78, 0x43, 0x70, 0x4A,
        0x6A, 0x66, 0x54, 0x7A, 0x65, 0x2F, 0x79, 0x6D, 0x65, 0x38, 0x64, 0x33,
        0x70, 0x61, 0x35, 0x51, 0x4B, 0x42, 0x67, 0x51, 0x44, 0x51, 0x50, 0x35,
        0x6D, 0x42, 0x34, 0x6A, 0x49, 0x2B, 0x67, 0x33, 0x58, 0x48, 0x33, 0x4D,
        0x75, 0x4C, 0x79, 0x42, 0x6A, 0x4D, 0x6F, 0x54, 0x49, 0x76, 0x6F, 0x79,
        0x37, 0x43, 0x59, 0x4D, 0x68, 0x5A, 0x0A, 0x36, 0x2F, 0x2B, 0x4B, 0x6B,
        0x70, 0x77, 0x31, 0x33, 0x32, 0x4A, 0x31, 0x36, 0x6D, 0x71, 0x6B, 0x4C,
        0x72, 0x77, 0x55, 0x4F, 0x5A, 0x66, 0x54, 0x30, 0x65, 0x31, 0x72, 0x4A,
        0x42, 0x73, 0x43, 0x55, 0x6B, 0x45, 0x6F, 0x42, 0x6D, 0x67, 0x4B, 0x4E,
        0x74, 0x52, 0x6B, 0x48, 0x6F, 0x33, 0x2F, 0x53, 0x6A, 0x55, 0x49, 0x2F,
        0x39, 0x66, 0x48, 0x6A, 0x33, 0x75, 0x53, 0x74, 0x50, 0x48, 0x56, 0x0A,
        0x6F, 0x50, 0x63, 0x66, 0x58, 0x6A, 0x2F, 0x67, 0x46, 0x52, 0x55, 0x6B,
        0x44, 0x44, 0x7A, 0x59, 0x2B, 0x61, 0x75, 0x42, 0x33, 0x64, 0x48, 0x4F,
        0x4E, 0x46, 0x31, 0x55, 0x31, 0x7A, 0x30, 0x36, 0x45, 0x41, 0x4E, 0x6B,
        0x6B, 0x50, 0x43, 0x43, 0x33, 0x61, 0x35, 0x33, 0x38, 0x55, 0x41, 0x4E,
        0x42, 0x49, 0x61, 0x50, 0x6A, 0x77, 0x70, 0x52, 0x64, 0x42, 0x7A, 0x4E,
        0x77, 0x31, 0x78, 0x6C, 0x0A, 0x62, 0x76, 0x6E, 0x35, 0x61, 0x43, 0x74,
        0x33, 0x48, 0x77, 0x4B, 0x42, 0x67, 0x42, 0x66, 0x4F, 0x6C, 0x34, 0x6A,
        0x47, 0x45, 0x58, 0x59, 0x6D, 0x4E, 0x36, 0x4B, 0x2B, 0x75, 0x30, 0x65,
        0x62, 0x71, 0x52, 0x44, 0x6B, 0x74, 0x32, 0x67, 0x49, 0x6F, 0x57, 0x36,
        0x62, 0x46, 0x6F, 0x37, 0x58, 0x64, 0x36, 0x78, 0x63, 0x69, 0x2F, 0x67,
        0x46, 0x57, 0x6A, 0x6F, 0x56, 0x43, 0x4F, 0x42, 0x59, 0x0A, 0x67, 0x43,
        0x38, 0x47, 0x4C, 0x4D, 0x6E, 0x77, 0x33, 0x7A, 0x32, 0x71, 0x67, 0x61,
        0x76, 0x34, 0x63, 0x51, 0x49, 0x67, 0x38, 0x45, 0x44, 0x59, 0x70, 0x62,
        0x70, 0x45, 0x34, 0x46, 0x48, 0x51, 0x6E, 0x6E, 0x74, 0x50, 0x6B, 0x4B,
        0x57, 0x2F, 0x62, 0x72, 0x75, 0x30, 0x4E, 0x74, 0x33, 0x79, 0x61, 0x4E,
        0x62, 0x38, 0x69, 0x67, 0x79, 0x31, 0x61, 0x5A, 0x4F, 0x52, 0x66, 0x49,
        0x76, 0x5A, 0x0A, 0x71, 0x54, 0x4D, 0x4C, 0x45, 0x33, 0x6D, 0x65, 0x6C,
        0x63, 0x5A, 0x57, 0x37, 0x4C, 0x61, 0x69, 0x71, 0x65, 0x4E, 0x31, 0x56,
        0x30, 0x76, 0x48, 0x2F, 0x4D, 0x43, 0x55, 0x64, 0x70, 0x58, 0x39, 0x59,
        0x31, 0x34, 0x4B, 0x39, 0x43, 0x4A, 0x59, 0x78, 0x7A, 0x73, 0x52, 0x4F,
        0x67, 0x50, 0x71, 0x64, 0x45, 0x67, 0x4D, 0x57, 0x59, 0x44, 0x46, 0x41,
        0x6F, 0x47, 0x41, 0x41, 0x65, 0x39, 0x6C, 0x0A, 0x58, 0x4D, 0x69, 0x65,
        0x55, 0x4F, 0x68, 0x6C, 0x30, 0x73, 0x71, 0x68, 0x64, 0x5A, 0x59, 0x52,
        0x62, 0x4F, 0x31, 0x65, 0x69, 0x77, 0x54, 0x49, 0x4C, 0x58, 0x51, 0x36,
        0x79, 0x47, 0x4D, 0x69, 0x42, 0x38, 0x61, 0x65, 0x2F, 0x76, 0x30, 0x70,
        0x62, 0x42, 0x45, 0x57, 0x6C, 0x70, 0x6E, 0x38, 0x6B, 0x32, 0x2B, 0x4A,
        0x6B, 0x71, 0x56, 0x54, 0x77, 0x48, 0x67, 0x67, 0x62, 0x43, 0x41, 0x5A,
        0x0A, 0x6A, 0x4F, 0x61, 0x71, 0x56, 0x74, 0x58, 0x31, 0x6D, 0x55, 0x79,
        0x54, 0x59, 0x7A, 0x6A, 0x73, 0x54, 0x7A, 0x34, 0x5A, 0x59, 0x6A, 0x68,
        0x61, 0x48, 0x4A, 0x33, 0x6A, 0x31, 0x57, 0x6C, 0x65, 0x67, 0x6F, 0x4D,
        0x63, 0x73, 0x74, 0x64, 0x66, 0x54, 0x2B, 0x74, 0x78, 0x4D, 0x55, 0x37,
        0x34, 0x6F, 0x67, 0x64, 0x4F, 0x71, 0x4D, 0x7A, 0x68, 0x78, 0x53, 0x55,
        0x4F, 0x34, 0x35, 0x67, 0x38, 0x0A, 0x66, 0x39, 0x57, 0x38, 0x39, 0x6D,
        0x70, 0x61, 0x38, 0x62, 0x42, 0x6A, 0x4F, 0x50, 0x75, 0x2B, 0x79, 0x46,
        0x79, 0x36, 0x36, 0x74, 0x44, 0x61, 0x5A, 0x36, 0x73, 0x57, 0x45, 0x37,
        0x63, 0x35, 0x53, 0x58, 0x45, 0x48, 0x58, 0x6C, 0x38, 0x43, 0x67, 0x59,
        0x45, 0x41, 0x74, 0x41, 0x57, 0x77, 0x46, 0x50, 0x6F, 0x44, 0x53, 0x54,
        0x64, 0x7A, 0x6F, 0x58, 0x41, 0x77, 0x52, 0x6F, 0x66, 0x30, 0x0A, 0x51,
        0x4D, 0x4F, 0x30, 0x38, 0x2B, 0x50, 0x6E, 0x51, 0x47, 0x6F, 0x50, 0x62,
        0x4D, 0x4A, 0x54, 0x71, 0x72, 0x67, 0x78, 0x72, 0x48, 0x59, 0x43, 0x53,
        0x38, 0x75, 0x34, 0x63, 0x59, 0x53, 0x48, 0x64, 0x44, 0x4D, 0x4A, 0x44,
        0x43, 0x4F, 0x4D, 0x6F, 0x35, 0x67, 0x46, 0x58, 0x79, 0x43, 0x2B, 0x35,
        0x46, 0x66, 0x54, 0x69, 0x47, 0x77, 0x42, 0x68, 0x79, 0x35, 0x38, 0x7A,
        0x35, 0x62, 0x37, 0x0A, 0x67, 0x42, 0x77, 0x46, 0x4B, 0x49, 0x39, 0x52,
        0x67, 0x52, 0x66, 0x56, 0x31, 0x44, 0x2F, 0x4E, 0x69, 0x6D, 0x78, 0x50,
        0x72, 0x6C, 0x6A, 0x33, 0x57, 0x48, 0x79, 0x65, 0x63, 0x31, 0x2F, 0x43,
        0x73, 0x2B, 0x42, 0x72, 0x2B, 0x2F, 0x76, 0x65, 0x6B, 0x4D, 0x56, 0x46,
        0x67, 0x35, 0x67, 0x65, 0x6B, 0x65, 0x48, 0x72, 0x34, 0x61, 0x47, 0x53,
        0x46, 0x34, 0x62, 0x6B, 0x30, 0x41, 0x6A, 0x56, 0x0A, 0x54, 0x76, 0x2F,
        0x70, 0x51, 0x6A, 0x79, 0x52, 0x75, 0x5A, 0x41, 0x74, 0x36, 0x36, 0x49,
        0x62, 0x52, 0x5A, 0x64, 0x6C, 0x32, 0x49, 0x49, 0x3D, 0x0A, 0x2D, 0x2D,
        0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41,
        0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D
    };
    /*
     * PEM of pem_rsa_priv_key:
     * -----BEGIN PRIVATE KEY-----
     * MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEkC4ZWv3ucFbU
     * F8YwlUrmQlLCZwAgr4DPUAFVHl+wFcXypVgScVY4K7QmdWKsYqb8tpOxqw0NwZWX
     * O+ta4+y27COufoOhRTMwNyN2LwSNTPN3eEk4ee5QnppEyDrqoCgvTlAAdToFaXvj
     * x13Ybj7jfhwN74qKdqsSEtPWyggeotiQSPy6KyBIuWtIxPAA8jAvfAnQj1eXhghF
     * N2NxkqgxvBYdNy1m3+jXACPLRzc11ZbNHKiwhCY1/HiSBkwHlIK+/VLj2smCKdUQ
     * gvLXSnnVgQulHioD6UgY8xA2a4M1rhYuTV8BrPRZ4BFx2o0jYWvGbA/Hlp7fTOy+
     * F5OkiHS7AgMBAAECggEAYgCu81ZiQBVDvWiDGKr+1pIf2CxprGJEm1h86ZcEx3L7
     * qFDW+g8HGWd04S3qvh9LublAJzetAPxRXL9zx3PXjJZs7e3HLEuny3TaWez0XI0O
     * 4LSY8S8d6pVBPmUEtwGWN4vYqHnKLXOb4QQAXs4MzfkM/Me/b+zdu1umwjMl3Dud
     * 5rVhkgvt8uhDUG3XSHeoJYBMbT9ikJDVMJ51rre/1Riddgxp8SktVkvGmMl9kQR8
     * 8dv3Px/kTN94EuRg0CkXBhHpoGo4qnM3Q3B5PlmSK5gkuPvWy9l8L/TVt8Lb6/zL
     * ByQW+g02wxeNGhw1fkD+XFH7KkSeWl+QnrLcePM0hQKBgQDxoqUk0PLOY5WgOkgr
     * umgie/K1WKs+izTtApjzcM76sza63b5R9w+P+NssMV4aeV9epEGZO68IUmi0QjvQ
     * npluQoadFYweFwSQ11BXHoeQBA4nNpkrV58hgzZN3m9JLR7JxyrIqXsRnUzl13Kj
     * GzZBCJxCpJjfTze/yme8d3pa5QKBgQDQP5mB4jI+g3XH3MuLyBjMoTIvoy7CYMhZ
     * 6/+Kkpw132J16mqkLrwUOZfT0e1rJBsCUkEoBmgKNtRkHo3/SjUI/9fHj3uStPHV
     * oPcfXj/gFRUkDDzY+auB3dHONF1U1z06EANkkPCC3a538UANBIaPjwpRdBzNw1xl
     * bvn5aCt3HwKBgBfOl4jGEXYmN6K+u0ebqRDkt2gIoW6bFo7Xd6xci/gFWjoVCOBY
     * gC8GLMnw3z2qgav4cQIg8EDYpbpE4FHQnntPkKW/bru0Nt3yaNb8igy1aZORfIvZ
     * qTMLE3melcZW7LaiqeN1V0vH/MCUdpX9Y14K9CJYxzsROgPqdEgMWYDFAoGAAe9l
     * XMieUOhl0sqhdZYRbO1eiwTILXQ6yGMiB8ae/v0pbBEWlpn8k2+JkqVTwHggbCAZ
     * jOaqVtX1mUyTYzjsTz4ZYjhaHJ3j1WlegoMcstdfT+txMU74ogdOqMzhxSUO45g8
     * f9W89mpa8bBjOPu+yFy66tDaZ6sWE7c5SXEHXl8CgYEAtAWwFPoDSTdzoXAwRof0
     * QMO08+PnQGoPbMJTqrgxrHYCS8u4cYSHdDMJDCOMo5gFXyC+5FfTiGwBhy58z5b7
     * gBwFKI9RgRfV1D/NimxPrlj3WHyec1/Cs+Br+/vekMVFg5gekeHr4aGSF4bk0AjV
     * Tv/pQjyRuZAt66IbRZdl2II=
     * -----END PRIVATE KEY-----
     */

    /* Load private key BIO, DER-encoded public key and PKCS#8 private key for testing */
    if (!TEST_ptr(bio_priv = BIO_new(BIO_s_mem()))
        || !TEST_int_gt(BIO_write(bio_priv, pem_rsa_priv_key, sizeof(pem_rsa_priv_key)), 0)
        || !TEST_ptr(pkey = PEM_read_bio_PrivateKey_ex(bio_priv, NULL, NULL, NULL, NULL, NULL))
        || !TEST_int_ge(BIO_seek(bio_priv, 0), 0)
        || !TEST_int_gt((len_pub = i2d_PUBKEY(pkey, &encoded_pub)), 0)
        || !TEST_ptr(p8 = EVP_PKEY2PKCS8(pkey)))
        goto end;
    EVP_PKEY_free(pkey);
    pkey = NULL;

    for (i = 0; i < OSSL_NELEM(properties_test); i++) {
        const char *libctx_prop = properties_test[i].provider_props;
        const char *explicit_prop = properties_test[i].explicit_props;
        /* *curr_provider will be updated in reset_ctx_providers */
        OSSL_PROVIDER **curr_provider = &providers[properties_test[i].curr_provider_idx];

        /*
         * Decoding a PEM-encoded key uses the properties to select the right provider.
         * Using a PEM-encoding adds an extra decoder before the key is created.
         */
        if (!TEST_int_eq(reset_ctx_providers(&my_libctx, providers, libctx_prop), 1))
            goto end;
        if (!TEST_int_ge(BIO_seek(bio_priv, 0), 0)
            || !TEST_ptr(pkey = PEM_read_bio_PrivateKey_ex(bio_priv, NULL, NULL, NULL, my_libctx,
                                                           explicit_prop))
            || !TEST_ptr_eq(EVP_PKEY_get0_provider(pkey), *curr_provider))
            goto end;
        EVP_PKEY_free(pkey);
        pkey = NULL;

        /* Decoding a DER-encoded X509_PUBKEY uses the properties to select the right provider */
        if (!TEST_int_eq(reset_ctx_providers(&my_libctx, providers, libctx_prop), 1))
            goto end;
        p = encoded_pub;
        if (!TEST_ptr(pkey = d2i_PUBKEY_ex(NULL, &p, len_pub, my_libctx, explicit_prop))
            || !TEST_ptr_eq(EVP_PKEY_get0_provider(pkey), *curr_provider))
            goto end;
        EVP_PKEY_free(pkey);
        pkey = NULL;

        /* Decoding a PKCS8_PRIV_KEY_INFO uses the properties to select the right provider */
        if (!TEST_int_eq(reset_ctx_providers(&my_libctx, providers, libctx_prop), 1))
            goto end;
        if (!TEST_ptr(pkey = EVP_PKCS82PKEY_ex(p8, my_libctx, explicit_prop))
            || !TEST_ptr_eq(EVP_PKEY_get0_provider(pkey), *curr_provider))
            goto end;
        EVP_PKEY_free(pkey);
        pkey = NULL;
    }

    ret = 1;

end:
    PKCS8_PRIV_KEY_INFO_free(p8);
    BIO_free(bio_priv);
    OPENSSL_free(encoded_pub);
    EVP_PKEY_free(pkey);
    OSSL_PROVIDER_unload(providers[DEFAULT_PROVIDER_IDX]);
    fake_rsa_finish(providers[FAKE_RSA_PROVIDER_IDX]);
    OSSL_LIB_CTX_free(my_libctx);
    return ret;
}

int setup_tests(void)
{
    libctx = OSSL_LIB_CTX_new();
    if (libctx == NULL)
        return 0;

    ADD_TEST(test_pkey_sig);
    ADD_TEST(test_alternative_keygen_init);
    ADD_TEST(test_pkey_eq);
    ADD_ALL_TESTS(test_pkey_store, 2);
    ADD_TEST(test_pkey_delete);
    ADD_TEST(test_pkey_store_open_ex);
    ADD_TEST(test_pkey_provider_decoder_props);

    return 1;
}

void cleanup_tests(void)
{
    OSSL_LIB_CTX_free(libctx);
}
