/* test_slhdsa.c
 *
 * Copyright (C) 2006-2026 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

#include <tests/unit.h>

#ifdef NO_INLINE
    #include <wolfssl/wolfcrypt/misc.h>
#else
    #define WOLFSSL_MISC_INCLUDED
    #include <wolfcrypt/src/misc.c>
#endif

#ifdef WOLFSSL_HAVE_SLHDSA
    #include <wolfssl/wolfcrypt/wc_slhdsa.h>
#endif
#include <wolfssl/wolfcrypt/types.h>
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <tests/api/api.h>
#include <tests/api/test_slhdsa.h>


#ifdef WOLFSSL_HAVE_SLHDSA
/* Pick the first available parameter set so tests that just need any one valid
 * SLH-DSA configuration compile and run in SHAKE-only, SHA-2-only, or mixed
 * builds. Preference order: SHAKE 128s/f, 192s/f, 256s/f, then the SHA-2
 * variants in the same order. */
#if defined(WOLFSSL_SLHDSA_PARAM_128S)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHAKE128S
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHAKE128S_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHAKE128S_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHAKE128S_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHAKE128S_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_128F)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHAKE128F
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHAKE128F_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHAKE128F_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHAKE128F_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHAKE128F_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_192S)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHAKE192S
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHAKE192S_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHAKE192S_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHAKE192S_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHAKE192S_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_192F)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHAKE192F
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHAKE192F_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHAKE192F_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHAKE192F_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHAKE192F_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_256S)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHAKE256S
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHAKE256S_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHAKE256S_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHAKE256S_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHAKE256S_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_256F)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHAKE256F
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHAKE256F_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHAKE256F_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHAKE256F_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHAKE256F_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHA2_128S
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHA2_128S_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHA2_128S_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHA2_128S_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHA2_128S_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHA2_128F
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHA2_128F_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHA2_128F_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHA2_128F_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHA2_128F_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHA2_192S
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHA2_192S_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHA2_192S_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHA2_192S_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHA2_192S_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHA2_192F
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHA2_192F_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHA2_192F_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHA2_192F_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHA2_192F_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHA2_256S
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHA2_256S_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHA2_256S_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHA2_256S_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHA2_256S_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F)
    #define TEST_SLHDSA_DEFAULT_PARAM     SLHDSA_SHA2_256F
    #define TEST_SLHDSA_DEFAULT_SIG_LEN   WC_SLHDSA_SHA2_256F_SIG_LEN
    #define TEST_SLHDSA_DEFAULT_PRIV_LEN  WC_SLHDSA_SHA2_256F_PRIV_LEN
    #define TEST_SLHDSA_DEFAULT_PUB_LEN   WC_SLHDSA_SHA2_256F_PUB_LEN
    #define TEST_SLHDSA_DEFAULT_SEED_LEN  WC_SLHDSA_SHA2_256F_SEED_LEN
#endif
#endif /* WOLFSSL_HAVE_SLHDSA */


/*
 * Test basic init/free and NULL parameter handling for SLH-DSA key operations.
 */
int test_wc_slhdsa(void)
{
    EXPECT_DECLS;
#ifdef WOLFSSL_HAVE_SLHDSA
    /* `key` is only used by the per-variant Init/Free blocks below, so
     * gate its declaration on the same precondition (at least one
     * parameter set compiled in) to avoid -Wunused-variable when SLH-DSA
     * is enabled but no params are. */
#ifdef TEST_SLHDSA_DEFAULT_PARAM
    SlhDsaKey key;

    /* Test NULL parameter handling for init. Use whichever variant the
     * build actually has so a SHA-2-only build doesn't pass a SHAKE param
     * id and conflate BAD_FUNC_ARG (NULL key) with NOT_COMPILED_IN
     * (variant disabled). */
    ExpectIntEQ(wc_SlhDsaKey_Init(NULL, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
#endif

    /* Test wc_SlhDsaKey_Free with NULL - should not crash. */
    wc_SlhDsaKey_Free(NULL);

    /* Test valid init for each supported parameter set. Each block zeros
     * `key` first so a future regression where wc_SlhDsaKey_Free leaves
     * a residual field set cannot be papered over by the next Init's
     * partial reinitialisation. */
#ifdef WOLFSSL_SLHDSA_PARAM_128S
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128S, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128F, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192S, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192F, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256S, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256F, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_SHA2
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128S
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128S, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128F
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128F, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192S
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192S, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192F
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192F, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256S
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256S, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256F
    XMEMSET(&key, 0, sizeof(key));
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256F, NULL, INVALID_DEVID),
        0);
    wc_SlhDsaKey_Free(&key);
#endif
#endif /* WOLFSSL_SLHDSA_SHA2 */

#endif /* WOLFSSL_HAVE_SLHDSA */
    return EXPECT_RESULT();
}

/*
 * Test size functions for SLH-DSA.
 */
int test_wc_slhdsa_sizes(void)
{
    EXPECT_DECLS;
#ifdef WOLFSSL_HAVE_SLHDSA
    /* See test_wc_slhdsa() for the rationale on this guard. */
#ifdef TEST_SLHDSA_DEFAULT_PARAM
    SlhDsaKey key;
#endif

    /* Test NULL parameter handling for size functions. */
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SigSize(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* Test sizes for each parameter set. */
#ifdef WOLFSSL_SLHDSA_PARAM_128S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128S, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHAKE128S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHAKE128S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHAKE128S_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHAKE128S),
        WC_SLHDSA_SHAKE128S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHAKE128S),
        WC_SLHDSA_SHAKE128S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHAKE128S),
        WC_SLHDSA_SHAKE128S_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_128F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128F, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHAKE128F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHAKE128F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHAKE128F_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHAKE128F),
        WC_SLHDSA_SHAKE128F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHAKE128F),
        WC_SLHDSA_SHAKE128F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHAKE128F),
        WC_SLHDSA_SHAKE128F_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_192S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192S, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHAKE192S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHAKE192S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHAKE192S_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHAKE192S),
        WC_SLHDSA_SHAKE192S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHAKE192S),
        WC_SLHDSA_SHAKE192S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHAKE192S),
        WC_SLHDSA_SHAKE192S_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_192F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192F, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHAKE192F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHAKE192F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHAKE192F_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHAKE192F),
        WC_SLHDSA_SHAKE192F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHAKE192F),
        WC_SLHDSA_SHAKE192F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHAKE192F),
        WC_SLHDSA_SHAKE192F_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_256S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256S, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHAKE256S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHAKE256S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHAKE256S_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHAKE256S),
        WC_SLHDSA_SHAKE256S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHAKE256S),
        WC_SLHDSA_SHAKE256S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHAKE256S),
        WC_SLHDSA_SHAKE256S_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_256F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256F, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHAKE256F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHAKE256F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHAKE256F_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHAKE256F),
        WC_SLHDSA_SHAKE256F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHAKE256F),
        WC_SLHDSA_SHAKE256F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHAKE256F),
        WC_SLHDSA_SHAKE256F_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_SHA2
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128S, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHA2_128S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHA2_128S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHA2_128S_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHA2_128S),
        WC_SLHDSA_SHA2_128S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHA2_128S),
        WC_SLHDSA_SHA2_128S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHA2_128S),
        WC_SLHDSA_SHA2_128S_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128F, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHA2_128F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHA2_128F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHA2_128F_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHA2_128F),
        WC_SLHDSA_SHA2_128F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHA2_128F),
        WC_SLHDSA_SHA2_128F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHA2_128F),
        WC_SLHDSA_SHA2_128F_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192S, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHA2_192S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHA2_192S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHA2_192S_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHA2_192S),
        WC_SLHDSA_SHA2_192S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHA2_192S),
        WC_SLHDSA_SHA2_192S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHA2_192S),
        WC_SLHDSA_SHA2_192S_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192F, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHA2_192F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHA2_192F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHA2_192F_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHA2_192F),
        WC_SLHDSA_SHA2_192F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHA2_192F),
        WC_SLHDSA_SHA2_192F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHA2_192F),
        WC_SLHDSA_SHA2_192F_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256S, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHA2_256S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHA2_256S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHA2_256S_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHA2_256S),
        WC_SLHDSA_SHA2_256S_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHA2_256S),
        WC_SLHDSA_SHA2_256S_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHA2_256S),
        WC_SLHDSA_SHA2_256S_SIG_LEN);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256F, NULL, INVALID_DEVID),
        0);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSize(&key), WC_SLHDSA_SHA2_256F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSize(&key), WC_SLHDSA_SHA2_256F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSize(&key), WC_SLHDSA_SHA2_256F_SIG_LEN);
    wc_SlhDsaKey_Free(&key);

#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    ExpectIntEQ(wc_SlhDsaKey_PrivateSizeFromParam(SLHDSA_SHA2_256F),
        WC_SLHDSA_SHA2_256F_PRIV_LEN);
#endif
    ExpectIntEQ(wc_SlhDsaKey_PublicSizeFromParam(SLHDSA_SHA2_256F),
        WC_SLHDSA_SHA2_256F_PUB_LEN);
    ExpectIntEQ(wc_SlhDsaKey_SigSizeFromParam(SLHDSA_SHA2_256F),
        WC_SLHDSA_SHA2_256F_SIG_LEN);
#endif
#endif /* WOLFSSL_SLHDSA_SHA2 */

#endif /* WOLFSSL_HAVE_SLHDSA */
    return EXPECT_RESULT();
}

/*
 * Test key generation for SLH-DSA.
 */
int test_wc_slhdsa_make_key(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    SlhDsaKey key;
    WC_RNG rng;

    XMEMSET(&rng, 0, sizeof(WC_RNG));
    ExpectIntEQ(wc_InitRng(&rng), 0);

    /* Test NULL parameter handling. */
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(NULL, &rng),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));

#ifdef WOLFSSL_SLHDSA_PARAM_128S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, NULL),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_128F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_192S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_192F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_256S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_256F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_SHA2
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#endif /* WOLFSSL_SLHDSA_SHA2 */

    /* Test MakeKeyWithRandom. */
#ifdef TEST_SLHDSA_DEFAULT_PARAM
    {
        byte sk_seed[TEST_SLHDSA_DEFAULT_SEED_LEN];
        byte sk_prf[TEST_SLHDSA_DEFAULT_SEED_LEN];
        byte pk_seed[TEST_SLHDSA_DEFAULT_SEED_LEN];

        XMEMSET(sk_seed, 0x01, sizeof(sk_seed));
        XMEMSET(sk_prf, 0x02, sizeof(sk_prf));
        XMEMSET(pk_seed, 0x03, sizeof(pk_seed));

        /* Test NULL parameter handling. */
        ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(NULL, sk_seed,
            sizeof(sk_seed), sk_prf, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
            WC_NO_ERR_TRACE(BAD_FUNC_ARG));

        ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
            INVALID_DEVID), 0);
        ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, NULL, sizeof(sk_seed),
            sk_prf, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
            WC_NO_ERR_TRACE(BAD_FUNC_ARG));
        ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed,
            sizeof(sk_seed), NULL, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
            WC_NO_ERR_TRACE(BAD_FUNC_ARG));
        ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed,
            sizeof(sk_seed), sk_prf, sizeof(sk_prf), NULL, sizeof(pk_seed)),
            WC_NO_ERR_TRACE(BAD_FUNC_ARG));
        /* Test wrong size. */
        ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed, 8,
            sk_prf, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
            WC_NO_ERR_TRACE(BAD_FUNC_ARG));

        ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed,
            sizeof(sk_seed), sk_prf, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
            0);
        wc_SlhDsaKey_Free(&key);
    }
#endif

    wc_FreeRng(&rng);
#endif /* WOLFSSL_HAVE_SLHDSA && !WOLFSSL_SLHDSA_VERIFY_ONLY */
    return EXPECT_RESULT();
}

/*
 * Test signing for SLH-DSA.
 */
int test_wc_slhdsa_sign(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    SlhDsaKey key;
    WC_RNG rng;
    byte msg[64];
    byte* sig = NULL;
    word32 sigLen;
    word32 expSigLen;
    byte ctx[10];

    sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(sig);

    XMEMSET(&rng, 0, sizeof(WC_RNG));
    XMEMSET(msg, 0xAA, sizeof(msg));
    XMEMSET(ctx, 0x01, sizeof(ctx));

    ExpectIntEQ(wc_InitRng(&rng), 0);

    /* Test NULL parameter handling. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(NULL, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    expSigLen = TEST_SLHDSA_DEFAULT_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), NULL, sizeof(msg),
        sig, &sigLen, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        NULL, &sigLen, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, NULL, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* Test buffer too small. */
    sigLen = 10;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), WC_NO_ERR_TRACE(BAD_LENGTH_E));

    /* Test successful signing. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, expSigLen);

    /* Test signing with NULL context (allowed). */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, NULL, 0, msg, sizeof(msg),
        sig, &sigLen, &rng), 0);

    wc_SlhDsaKey_Free(&key);

    /* Test SignDeterministic. */
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignDeterministic(NULL, ctx, sizeof(ctx),
        msg, sizeof(msg), sig, &sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignDeterministic(&key, ctx, sizeof(ctx),
        msg, sizeof(msg), sig, &sigLen), 0);
    ExpectIntEQ(sigLen, expSigLen);

    wc_SlhDsaKey_Free(&key);

    /* Test SignWithRandom. */
    {
        byte addRnd[WC_SLHDSA_MAX_SEED];
        XMEMSET(addRnd, 0x55, sizeof(addRnd));

        ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
            INVALID_DEVID), 0);
        ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

        sigLen = WC_SLHDSA_MAX_SIG_LEN;
        ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(NULL, ctx, sizeof(ctx),
            msg, sizeof(msg), sig, &sigLen, addRnd),
            WC_NO_ERR_TRACE(BAD_FUNC_ARG));
        ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, ctx, sizeof(ctx),
            msg, sizeof(msg), sig, &sigLen, NULL),
            WC_NO_ERR_TRACE(BAD_FUNC_ARG));
        ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, ctx, sizeof(ctx),
            msg, sizeof(msg), sig, &sigLen, addRnd), 0);
        ExpectIntEQ(sigLen, expSigLen);

        wc_SlhDsaKey_Free(&key);
    }

    wc_FreeRng(&rng);
    XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif /* WOLFSSL_HAVE_SLHDSA && !WOLFSSL_SLHDSA_VERIFY_ONLY */
    return EXPECT_RESULT();
}

/*
 * Test verification for SLH-DSA.
 */
int test_wc_slhdsa_verify(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    SlhDsaKey key;
    WC_RNG rng;
    byte msg[64];
    byte* sig = NULL;
    word32 sigLen;
    byte ctx[10];

    sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(sig);

    XMEMSET(&rng, 0, sizeof(WC_RNG));
    XMEMSET(msg, 0xAA, sizeof(msg));
    XMEMSET(ctx, 0x01, sizeof(ctx));

    ExpectIntEQ(wc_InitRng(&rng), 0);

    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    /* Generate a signature. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);

    /* Test NULL parameter handling. */
    ExpectIntEQ(wc_SlhDsaKey_Verify(NULL, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), NULL, sizeof(msg),
        sig, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        NULL, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* Test successful verification. */
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);

    /* Test verification with wrong message. */
    msg[0] ^= 0xFF;
    ExpectIntNE(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    msg[0] ^= 0xFF;

    /* Test verification with wrong context. */
    ctx[0] ^= 0xFF;
    ExpectIntNE(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    ctx[0] ^= 0xFF;

    /* Test verification with corrupted signature. */
    sig[0] ^= 0xFF;
    ExpectIntNE(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    sig[0] ^= 0xFF;

    /* Test verification with NULL context (allowed, but must match signing). */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, NULL, 0, msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, NULL, 0, msg, sizeof(msg),
        sig, sigLen), 0);

    wc_SlhDsaKey_Free(&key);

    wc_FreeRng(&rng);
    XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif /* WOLFSSL_HAVE_SLHDSA */
    return EXPECT_RESULT();
}

/*
 * Test combined sign and verify for all parameter sets.
 */
int test_wc_slhdsa_sign_vfy(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    SlhDsaKey key;
    WC_RNG rng;
    byte msg[64];
    byte* sig = NULL;
    word32 sigLen;
    byte ctx[10];

    sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(sig);

    XMEMSET(&rng, 0, sizeof(WC_RNG));
    XMEMSET(msg, 0xAA, sizeof(msg));
    XMEMSET(ctx, 0x01, sizeof(ctx));

    ExpectIntEQ(wc_InitRng(&rng), 0);

#ifdef WOLFSSL_SLHDSA_PARAM_128S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHAKE128S_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);

    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_128F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHAKE128F_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);

    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_192S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, (word32)wc_SlhDsaKey_SigSize(&key));
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);

    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_192F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHAKE192F_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);

    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_256S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHAKE256S_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);

    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_PARAM_256F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHAKE256F_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);

    wc_SlhDsaKey_Free(&key);
#endif

#ifdef WOLFSSL_SLHDSA_SHA2
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHA2_128S_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHA2_128F_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHA2_192S_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHA2_192F_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256S, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHA2_256S_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256F, NULL, INVALID_DEVID),
        0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, WC_SLHDSA_SHA2_256F_SIG_LEN);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    wc_SlhDsaKey_Free(&key);
#endif
#endif /* WOLFSSL_SLHDSA_SHA2 */

    wc_FreeRng(&rng);
    XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif /* WOLFSSL_HAVE_SLHDSA */
    return EXPECT_RESULT();
}

/*
 * Test hash signing and verification for SLH-DSA.
 */
int test_wc_slhdsa_sign_hash(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    SlhDsaKey key;
    WC_RNG rng;
    byte hash[64];
    byte* sig = NULL;
    word32 sigLen;
    word32 expSigLen;
    byte ctx[10];

    sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(sig);

    XMEMSET(&rng, 0, sizeof(WC_RNG));
    XMEMSET(hash, 0xBB, sizeof(hash));
    XMEMSET(ctx, 0x01, sizeof(ctx));

    ExpectIntEQ(wc_InitRng(&rng), 0);

    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    expSigLen = TEST_SLHDSA_DEFAULT_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    /* Test SignHash NULL parameter handling. Use 32-byte hash length so the
     * NULL check trips before the digest-length check (HashSLH-DSA expects
     * SHA-256 digest = 32 bytes). */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignHash(NULL, ctx, sizeof(ctx), hash,
        32, WC_HASH_TYPE_SHA256, sig, &sigLen, &rng),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), NULL,
        32, WC_HASH_TYPE_SHA256, sig, &sigLen, &rng),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
        32, WC_HASH_TYPE_SHA256, NULL, &sigLen, &rng),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
        32, WC_HASH_TYPE_SHA256, sig, NULL, &rng),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
        32, WC_HASH_TYPE_SHA256, sig, &sigLen, NULL),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* HashSLH-DSA must reject digest lengths that don't match hashType. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 31,
        WC_HASH_TYPE_SHA256, sig, &sigLen, &rng),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 33,
        WC_HASH_TYPE_SHA256, sig, &sigLen, &rng),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
    /* Generate a real signature first so VerifyHash gets to its length check
     * rather than failing on signature size. */
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 31,
        WC_HASH_TYPE_SHA256, sig, sigLen),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 33,
        WC_HASH_TYPE_SHA256, sig, sigLen),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));

    /* Unsupported hashType (FIPS 205 doesn't list WC_HASH_TYPE_NONE) hits
     * the default branch of slhdsakey_validate_prehash. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_NONE, sig, &sigLen, &rng),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));

    /* Test SignHash with SHA-256. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), 0);
    ExpectIntEQ(sigLen, expSigLen);
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHA256, sig, sigLen), 0);

    /* Test VerifyHash NULL parameter handling. */
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(NULL, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHA256, sig, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), NULL, 32,
        WC_HASH_TYPE_SHA256, sig, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHA256, NULL, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* Test VerifyHash with wrong hash. */
    hash[0] ^= 0xFF;
    ExpectIntNE(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHA256, sig, sigLen), 0);
    hash[0] ^= 0xFF;

    /* Test SignHashDeterministic. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignHashDeterministic(NULL, ctx, sizeof(ctx),
        hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignHashDeterministic(&key, ctx, sizeof(ctx),
        hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen), 0);
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHA256, sig, sigLen), 0);

    /* Test SignHashWithRandom. */
    {
        byte addRnd[WC_SLHDSA_MAX_SEED];
        XMEMSET(addRnd, 0x55, sizeof(addRnd));

        sigLen = WC_SLHDSA_MAX_SIG_LEN;
        ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(NULL, ctx, sizeof(ctx),
            hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen, addRnd),
            WC_NO_ERR_TRACE(BAD_FUNC_ARG));
        ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, ctx, sizeof(ctx),
            hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen, NULL),
            WC_NO_ERR_TRACE(BAD_FUNC_ARG));
        ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, ctx, sizeof(ctx),
            hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen, addRnd), 0);
        ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
            WC_HASH_TYPE_SHA256, sig, sigLen), 0);
    }

#ifdef WOLFSSL_SHA512
    /* SHA-512 round-trip exercises a 64-byte digest path. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 64,
        WC_HASH_TYPE_SHA512, sig, &sigLen, &rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 64,
        WC_HASH_TYPE_SHA512, sig, sigLen), 0);
    /* SHA-512 must also reject the wrong digest length. */
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHA512, sig, &sigLen, &rng),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
#endif

#ifdef WOLFSSL_SHAKE128
    /* SHAKE128 PHM is fixed at 256 bits per FIPS 205 Section 10.2.2. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHAKE128, sig, &sigLen, &rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHAKE128, sig, sigLen), 0);
    /* SignHash and VerifyHash both reject mismatched digest length. */
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 64,
        WC_HASH_TYPE_SHAKE128, sig, &sigLen, &rng),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 64,
        WC_HASH_TYPE_SHAKE128, sig, sigLen),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
#endif

#ifdef WOLFSSL_SHAKE256
    /* SHAKE256 PHM is fixed at 512 bits per FIPS 205 Section 10.2.2. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 64,
        WC_HASH_TYPE_SHAKE256, sig, &sigLen, &rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 64,
        WC_HASH_TYPE_SHAKE256, sig, sigLen), 0);
    /* SignHash and VerifyHash both reject mismatched digest length. */
    ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHAKE256, sig, &sigLen, &rng),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
    ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
        WC_HASH_TYPE_SHAKE256, sig, sigLen),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
#endif

    wc_SlhDsaKey_Free(&key);

    wc_FreeRng(&rng);
    XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif /* WOLFSSL_HAVE_SLHDSA */
    return EXPECT_RESULT();
}

/*
 * Test the FIPS 205 internal interface (M' supplied directly) for SLH-DSA.
 * Covers wc_SlhDsaKey_SignMsgDeterministic, wc_SlhDsaKey_SignMsgWithRandom,
 * and wc_SlhDsaKey_VerifyMsg, plus a HashSLH-DSA equivalence cross-check
 * that proves an externally-built M' matches the SignHash/VerifyHash path.
 */
int test_wc_slhdsa_sign_msg(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
    !defined(NO_SHA256)
    SlhDsaKey key;
    WC_RNG rng;
    byte mprime[64];
    byte* sig = NULL;
    word32 sigLen;
    byte addRnd[WC_SLHDSA_MAX_SEED];

    sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(sig);

    XMEMSET(&rng, 0, sizeof(WC_RNG));
    XMEMSET(mprime, 0xAA, sizeof(mprime));
    XMEMSET(addRnd, 0x55, sizeof(addRnd));

    ExpectIntEQ(wc_InitRng(&rng), 0);

    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    /* SignMsgDeterministic NULL-arg checks. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(NULL, mprime, sizeof(mprime),
        sig, &sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, NULL, sizeof(mprime),
        sig, &sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime, sizeof(mprime),
        NULL, &sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime, sizeof(mprime),
        sig, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* SignMsgDeterministic must reject sigSz smaller than params->sigLen. */
    sigLen = 1;
    ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime,
        sizeof(mprime), sig, &sigLen), WC_NO_ERR_TRACE(BAD_LENGTH_E));

    /* Round-trip: Deterministic. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime,
        sizeof(mprime), sig, &sigLen), 0);
    ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(&key, mprime, sizeof(mprime), sig,
        sigLen), 0);

    /* SignMsgWithRandom NULL-arg checks. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(NULL, mprime, sizeof(mprime),
        sig, &sigLen, addRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime),
        sig, &sigLen, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* SignMsgWithRandom must reject sigSz smaller than params->sigLen. */
    sigLen = 1;
    ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime),
        sig, &sigLen, addRnd), WC_NO_ERR_TRACE(BAD_LENGTH_E));

    /* Round-trip: WithRandom. Reset sigLen explicitly so the test doesn't
     * silently rely on the previous call having set it to params->sigLen. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime),
        sig, &sigLen, addRnd), 0);
    ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(&key, mprime, sizeof(mprime), sig,
        sigLen), 0);

    /* VerifyMsg NULL-arg checks. */
    ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(NULL, mprime, sizeof(mprime), sig,
        sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(&key, NULL, sizeof(mprime), sig,
        sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(&key, mprime, sizeof(mprime), NULL,
        sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* Equivalence cross-check: build M' = 0x01 || ctxSz || OID(SHA-256) ||
     * SHA256(orig) externally, sign it via SignMsgDeterministic, and verify
     * via VerifyHash with the same SHA-256 digest. Both paths must agree. */
    {
        static const byte sha256_oid[] = {
            0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
            0x04, 0x02, 0x01
        };
        static const byte orig[] = "Hello World!";
        byte digest[WC_SHA256_DIGEST_SIZE];
        byte built_mprime[2 + sizeof(sha256_oid) + WC_SHA256_DIGEST_SIZE];
        word32 idx = 0;
        word32 sigLen2;

        ExpectIntEQ(wc_Sha256Hash(orig, (word32)sizeof(orig) - 1, digest), 0);

        built_mprime[idx++] = 0x01;        /* HashSLH-DSA domain separator */
        built_mprime[idx++] = 0;           /* ctxSz = 0 */
        XMEMCPY(built_mprime + idx, sha256_oid, sizeof(sha256_oid));
        idx += (word32)sizeof(sha256_oid);
        XMEMCPY(built_mprime + idx, digest, sizeof(digest));
        idx += (word32)sizeof(digest);

        sigLen = WC_SLHDSA_MAX_SIG_LEN;
        ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, built_mprime,
            idx, sig, &sigLen), 0);

        /* The same signature must verify via the HashSLH-DSA external API. */
        ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, NULL, 0, digest,
            sizeof(digest), WC_HASH_TYPE_SHA256, sig, sigLen), 0);

        /* And the deterministic HashSLH-DSA path must produce the SAME
         * signature bytes (this is the strongest interop check). */
        sigLen2 = WC_SLHDSA_MAX_SIG_LEN;
        {
            byte* sig2 = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL,
                DYNAMIC_TYPE_TMP_BUFFER);
            ExpectNotNull(sig2);
            ExpectIntEQ(wc_SlhDsaKey_SignHashDeterministic(&key, NULL, 0,
                digest, sizeof(digest), WC_HASH_TYPE_SHA256, sig2,
                &sigLen2), 0);
            ExpectIntEQ(sigLen2, sigLen);
            ExpectIntEQ(XMEMCMP(sig2, sig, sigLen), 0);
            XFREE(sig2, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        }
    }

    wc_SlhDsaKey_Free(&key);
    wc_FreeRng(&rng);
    XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
    return EXPECT_RESULT();
}

/*
 * Test export and import for SLH-DSA keys.
 */
int test_wc_slhdsa_export_import(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    SlhDsaKey key;
    SlhDsaKey key2;
    WC_RNG rng;
    byte* privKey = NULL;
    byte* pubKey = NULL;
    word32 privKeyLen;
    word32 expPrivKeyLen;
    word32 pubKeyLen;
    word32 expPubKeyLen;
    byte msg[64];
    byte* sig = NULL;
    word32 sigLen;
    byte ctx[10];

    privKey = (byte*)XMALLOC(WC_SLHDSA_MAX_PRIV_LEN, NULL,
        DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(privKey);
    pubKey = (byte*)XMALLOC(WC_SLHDSA_MAX_PUB_LEN, NULL,
        DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(pubKey);
    sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(sig);

    XMEMSET(&rng, 0, sizeof(WC_RNG));
    XMEMSET(msg, 0xAA, sizeof(msg));
    XMEMSET(ctx, 0x01, sizeof(ctx));

    ExpectIntEQ(wc_InitRng(&rng), 0);

    /* Test NULL parameter handling for export functions. */
    privKeyLen = WC_SLHDSA_MAX_PRIV_LEN;
    pubKeyLen = WC_SLHDSA_MAX_PUB_LEN;
    ExpectIntEQ(wc_SlhDsaKey_ExportPrivate(NULL, privKey, &privKeyLen),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_ExportPublic(NULL, pubKey, &pubKeyLen),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* Test NULL parameter handling for import functions. */
    ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(NULL, privKey, privKeyLen),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_ImportPublic(NULL, pubKey, pubKeyLen),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    expPrivKeyLen = TEST_SLHDSA_DEFAULT_PRIV_LEN;
    expPubKeyLen = TEST_SLHDSA_DEFAULT_PUB_LEN;
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    /* Test export with NULL buffer. */
    ExpectIntEQ(wc_SlhDsaKey_ExportPrivate(&key, NULL, &privKeyLen),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_ExportPrivate(&key, privKey, NULL),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_ExportPublic(&key, NULL, &pubKeyLen),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_ExportPublic(&key, pubKey, NULL),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* Test export with buffer too small. */
    privKeyLen = 10;
    ExpectIntEQ(wc_SlhDsaKey_ExportPrivate(&key, privKey, &privKeyLen),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
    pubKeyLen = 10;
    ExpectIntEQ(wc_SlhDsaKey_ExportPublic(&key, pubKey, &pubKeyLen),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));

    /* Test successful export. */
    privKeyLen = WC_SLHDSA_MAX_PRIV_LEN;
    ExpectIntEQ(wc_SlhDsaKey_ExportPrivate(&key, privKey, &privKeyLen), 0);
    ExpectIntEQ(privKeyLen, expPrivKeyLen);

    pubKeyLen = WC_SLHDSA_MAX_PUB_LEN;
    ExpectIntEQ(wc_SlhDsaKey_ExportPublic(&key, pubKey, &pubKeyLen), 0);
    ExpectIntEQ(pubKeyLen, expPubKeyLen);

    /* Sign with original key. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);

    /* Test import into new key and verify. */
    ExpectIntEQ(wc_SlhDsaKey_Init(&key2, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);

    /* Test import with NULL data. */
    ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key2, NULL, privKeyLen),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_ImportPublic(&key2, NULL, pubKeyLen),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* Test import with wrong size. */
    ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key2, privKey, 10),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));
    ExpectIntEQ(wc_SlhDsaKey_ImportPublic(&key2, pubKey, 10),
        WC_NO_ERR_TRACE(BAD_LENGTH_E));

    /* Test successful import of public key only. */
    ExpectIntEQ(wc_SlhDsaKey_ImportPublic(&key2, pubKey, pubKeyLen), 0);
    /* Verify with imported public key. */
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key2, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);
    wc_SlhDsaKey_Free(&key2);

    /* Test import of private key. */
    ExpectIntEQ(wc_SlhDsaKey_Init(&key2, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key2, privKey, privKeyLen), 0);
    /* Sign with imported key. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&key2, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, &sigLen, &rng), 0);
    /* Verify with original key. */
    ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg),
        sig, sigLen), 0);

    wc_SlhDsaKey_Free(&key2);
    wc_SlhDsaKey_Free(&key);

    wc_FreeRng(&rng);
    XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    XFREE(pubKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif /* WOLFSSL_HAVE_SLHDSA */
    return EXPECT_RESULT();
}

/*
 * Test key check for SLH-DSA.
 */
int test_wc_slhdsa_check_key(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    SlhDsaKey key;
    WC_RNG rng;
    byte* privKey = NULL;
    byte* pubKey = NULL;
    word32 privKeyLen;
    word32 pubKeyLen;

    privKey = (byte*)XMALLOC(WC_SLHDSA_MAX_PRIV_LEN, NULL,
        DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(privKey);
    pubKey = (byte*)XMALLOC(WC_SLHDSA_MAX_PUB_LEN, NULL,
        DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(pubKey);

    XMEMSET(&rng, 0, sizeof(WC_RNG));
    ExpectIntEQ(wc_InitRng(&rng), 0);

    /* Test NULL parameter handling. */
    ExpectIntEQ(wc_SlhDsaKey_CheckKey(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0);

    /* Test check of valid key. */
    ExpectIntEQ(wc_SlhDsaKey_CheckKey(&key), 0);

    /* Export keys. */
    privKeyLen = WC_SLHDSA_MAX_PRIV_LEN;
    ExpectIntEQ(wc_SlhDsaKey_ExportPrivate(&key, privKey, &privKeyLen), 0);
    pubKeyLen = WC_SLHDSA_MAX_PUB_LEN;
    ExpectIntEQ(wc_SlhDsaKey_ExportPublic(&key, pubKey, &pubKeyLen), 0);

    wc_SlhDsaKey_Free(&key);

    /* Test check with only public key imported - requires private key. */
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_ImportPublic(&key, pubKey, pubKeyLen), 0);
    /* CheckKey requires a private key to validate. */
    ExpectIntEQ(wc_SlhDsaKey_CheckKey(&key), WC_NO_ERR_TRACE(MISSING_KEY));
    wc_SlhDsaKey_Free(&key);

    /* Test check with only private key imported. */
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key, privKey, privKeyLen), 0);
    ExpectIntEQ(wc_SlhDsaKey_CheckKey(&key), 0);
    wc_SlhDsaKey_Free(&key);

    /* Test check with both keys imported. */
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_ImportPublic(&key, pubKey, pubKeyLen), 0);
    ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key, privKey, privKeyLen), 0);
    ExpectIntEQ(wc_SlhDsaKey_CheckKey(&key), 0);

    key.sk[0] ^= 0x01;
    ExpectIntEQ(wc_SlhDsaKey_CheckKey(&key),
        WC_NO_ERR_TRACE(WC_KEY_MISMATCH_E));
    wc_SlhDsaKey_Free(&key);

    /* Regression: Private-then-Public order. ImportPrivate sets
     * flags = WC_SLHDSA_FLAG_BOTH_KEYS; if ImportPublic clobbered flags
     * with `=` instead of `|=`, the FLAG_PRIVATE bit would be dropped and
     * CheckKey would return MISSING_KEY. */
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
        INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key, privKey, privKeyLen), 0);
    ExpectIntEQ(wc_SlhDsaKey_ImportPublic(&key, pubKey, pubKeyLen), 0);
    ExpectIntEQ(wc_SlhDsaKey_CheckKey(&key), 0);
    wc_SlhDsaKey_Free(&key);

    wc_FreeRng(&rng);
    XFREE(pubKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif /* WOLFSSL_HAVE_SLHDSA */
    return EXPECT_RESULT();
}

#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
    defined(WC_ENABLE_ASYM_KEY_EXPORT)
/* Round-trip a single SLH-DSA parameter set through the DER codec:
 * generate -> KeyToDer -> PrivateKeyDecode -> sign/verify round-trip.
 * Also tests PublicKeyToDer -> PublicKeyDecode, and that the decode
 * correctly auto-detects the parameter set from the OID. */
static int slhdsa_der_roundtrip_one(enum SlhDsaParam param)
{
    EXPECT_DECLS;
    SlhDsaKey keyGen;
    SlhDsaKey keyPriv;
    SlhDsaKey keyPub;
    WC_RNG rng;
    byte* derBuf = NULL;
    byte* sig = NULL;
    const word32 derBufSz = 16 * 1024;
    word32 derLen = 0; /* initialize to suppress false -Wmaybe-uninitialized */
    word32 idx;
    word32 sigLen;
    enum SlhDsaParam placeholder = param;
    static const byte msg[] = "SLH-DSA DER round-trip";
    static const enum SlhDsaParam candidates[] = {
        SLHDSA_SHAKE256S, SLHDSA_SHAKE128F, SLHDSA_SHAKE192S,
        SLHDSA_SHAKE192F, SLHDSA_SHAKE256F, SLHDSA_SHAKE128S,
    #ifdef WOLFSSL_SLHDSA_SHA2
        SLHDSA_SHA2_128S, SLHDSA_SHA2_128F, SLHDSA_SHA2_192S,
        SLHDSA_SHA2_192F, SLHDSA_SHA2_256S, SLHDSA_SHA2_256F,
    #endif
    };
    size_t cIdx;

    /* Pick a placeholder different from the encoded param so a regression
     * that disables OID auto-detection would fail the post-decode equality
     * check. Walk the candidate list and probe each via wc_SlhDsaKey_Init;
     * the first one that initialises successfully (i.e. is compiled in) is
     * used. Falls back to the encoded param if no other variant is
     * available, in which case the test reduces to a smoke check. */
    for (cIdx = 0; cIdx < sizeof(candidates)/sizeof(candidates[0]); cIdx++) {
        SlhDsaKey probe;
        if (candidates[cIdx] == param) {
            continue;
        }
        XMEMSET(&probe, 0, sizeof(probe));
        if (wc_SlhDsaKey_Init(&probe, candidates[cIdx], NULL,
                              INVALID_DEVID) == 0) {
            placeholder = candidates[cIdx];
            wc_SlhDsaKey_Free(&probe);
            break;
        }
    }

    XMEMSET(&rng, 0, sizeof(rng));
    XMEMSET(&keyGen, 0, sizeof(keyGen));
    XMEMSET(&keyPriv, 0, sizeof(keyPriv));
    XMEMSET(&keyPub, 0, sizeof(keyPub));

    derBuf = (byte*)XMALLOC(derBufSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(derBuf);
    sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(sig);

    ExpectIntEQ(wc_InitRng(&rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_Init(&keyGen, param, NULL, INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&keyGen, &rng), 0);

    /* Size-query contract: passing output=NULL returns the encoded size
     * without touching the buffer. The real encode below must produce
     * exactly this many bytes -- a size-query regression (e.g. forgetting
     * to add verSz) would surface here as a mismatch. */
    {
        int querySize;
        ExpectIntGT(querySize = wc_SlhDsaKey_KeyToDer(&keyGen, NULL, 0), 0);
        ExpectIntGT(derLen = (word32)wc_SlhDsaKey_KeyToDer(&keyGen, derBuf,
            derBufSz), 0);
        ExpectIntEQ((int)derLen, querySize);

        /* BUFFER_E contract: too-small buffer is rejected without writing
         * anything past the limit. Pass inLen = querySize - 1 so the
         * length check fails for any encoding. */
        ExpectIntEQ(wc_SlhDsaKey_KeyToDer(&keyGen, derBuf,
            (word32)(querySize - 1)), WC_NO_ERR_TRACE(BUFFER_E));

        /* PrivateKeyToDer is an RFC 9909 alias of KeyToDer; sizes must
         * match and BUFFER_E must propagate. */
        ExpectIntEQ(wc_SlhDsaKey_PrivateKeyToDer(&keyGen, NULL, 0), querySize);
        ExpectIntEQ(wc_SlhDsaKey_PrivateKeyToDer(&keyGen, derBuf,
            (word32)(querySize - 1)), WC_NO_ERR_TRACE(BUFFER_E));
    }

    /* Decode into a fresh key.  The decode must auto-detect the real
     * parameter set from the OID embedded in the DER encoding. */
    ExpectIntEQ(wc_SlhDsaKey_Init(&keyPriv, placeholder, NULL, INVALID_DEVID),
        0);
    idx = 0;
    ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(derBuf, &idx, &keyPriv, derLen),
        0);
    /* Verify the decoded key reports the ORIGINAL parameter set. */
    if (keyPriv.params != NULL) {
        ExpectIntEQ((int)keyPriv.params->param, (int)param);
    }
    /* Byte-level equivalence check: re-encode the decoded private key
     * and compare against the original DER. This catches a regression
     * even in single-variant builds where placeholder == param made the
     * params equality test above tautological -- if the decoder ignored
     * the OID and kept stale state, the bytes won't match. */
    {
        byte* roundBuf = (byte*)XMALLOC(derBufSz, NULL,
            DYNAMIC_TYPE_TMP_BUFFER);
        word32 roundLen;
        ExpectNotNull(roundBuf);
        ExpectIntGT(roundLen = (word32)wc_SlhDsaKey_KeyToDer(&keyPriv,
            roundBuf, derBufSz), 0);
        ExpectIntEQ((int)roundLen, (int)derLen);
        if (roundBuf != NULL) {
            ExpectIntEQ(XMEMCMP(roundBuf, derBuf, roundLen), 0);
        }
        XFREE(roundBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    }

    /* Sign with the decoded private key and verify with the originally
     * generated key. This proves the decoded key material is correct. */
    sigLen = WC_SLHDSA_MAX_SIG_LEN;
    ExpectIntEQ(wc_SlhDsaKey_Sign(&keyPriv, NULL, 0, msg, (word32)sizeof(msg),
        sig, &sigLen, &rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_Verify(&keyGen, NULL, 0, msg, (word32)sizeof(msg),
        sig, sigLen), 0);

    /* Also test PrivateKeyToDer -> PrivateKeyDecode round-trip. */
    {
        SlhDsaKey keyPriv2;
        word32 derLen2;
        word32 idx2 = 0;
        XMEMSET(&keyPriv2, 0, sizeof(keyPriv2));
        ExpectIntGT(derLen2 = (word32)wc_SlhDsaKey_PrivateKeyToDer(&keyGen,
            derBuf, derBufSz), 0);
        ExpectIntEQ(wc_SlhDsaKey_Init(&keyPriv2, placeholder, NULL,
            INVALID_DEVID), 0);
        ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(derBuf, &idx2, &keyPriv2,
            derLen2), 0);
        /* Verify the PrivateKeyToDer output matches KeyToDer. */
        sigLen = WC_SLHDSA_MAX_SIG_LEN;
        ExpectIntEQ(wc_SlhDsaKey_Sign(&keyPriv2, NULL, 0, msg,
            (word32)sizeof(msg), sig, &sigLen, &rng), 0);
        ExpectIntEQ(wc_SlhDsaKey_Verify(&keyGen, NULL, 0, msg,
            (word32)sizeof(msg), sig, sigLen), 0);
        wc_SlhDsaKey_Free(&keyPriv2);
    }

    /* PKCS#8 v2 (RFC 5958) acceptance: the decoder explicitly allows
     * version=0 or version=1. The encoder only ever writes version=0,
     * so without a targeted check the v=1 branch would never fire and a
     * regression that rejected v2 wrappers (legal RFC 5958 OneAsymmetricKey
     * input from external tools) would slip through. Walk past the outer
     * SEQUENCE header (which uses short or long-form length depending on
     * the parameter set's encoded size) to land on the INTEGER version
     * field, then flip its value from 0 to 1. */
    {
        SlhDsaKey keyPrivV2;
        word32 idxV2 = 0;
        word32 verPos;
        byte saved;

        XMEMSET(&keyPrivV2, 0, sizeof(keyPrivV2));
        ExpectIntGT((int)derLen, 5);
        ExpectIntEQ((int)derBuf[0], 0x30); /* outer SEQUENCE tag */
        if ((derBuf[1] & 0x80) == 0) {
            verPos = 2;                       /* short-form length */
        }
        else {
            verPos = 2 + (derBuf[1] & 0x7F);  /* long-form length */
        }
        ExpectIntLT((int)verPos + 3, (int)derLen);
        ExpectIntEQ((int)derBuf[verPos], 0x02);     /* INTEGER tag */
        ExpectIntEQ((int)derBuf[verPos + 1], 0x01); /* INTEGER length */
        ExpectIntEQ((int)derBuf[verPos + 2], 0x00); /* v0 baseline */
        saved = derBuf[verPos + 2];
        derBuf[verPos + 2] = 0x01;

        ExpectIntEQ(wc_SlhDsaKey_Init(&keyPrivV2, placeholder, NULL,
            INVALID_DEVID), 0);
        ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(derBuf, &idxV2, &keyPrivV2,
            derLen), 0);
        if (keyPrivV2.params != NULL) {
            ExpectIntEQ((int)keyPrivV2.params->param, (int)param);
        }
        /* Confirm the v2-decoded private material is functionally
         * identical: a signature it produces verifies under keyGen. */
        sigLen = WC_SLHDSA_MAX_SIG_LEN;
        ExpectIntEQ(wc_SlhDsaKey_Sign(&keyPrivV2, NULL, 0, msg,
            (word32)sizeof(msg), sig, &sigLen, &rng), 0);
        ExpectIntEQ(wc_SlhDsaKey_Verify(&keyGen, NULL, 0, msg,
            (word32)sizeof(msg), sig, sigLen), 0);
        derBuf[verPos + 2] = saved;
        wc_SlhDsaKey_Free(&keyPrivV2);
    }

    /* Now round-trip the public key alone, with size-query and BUFFER_E
     * contract checks for both withAlg modes. */
    {
        int querySpki, queryRaw;
        byte rawPub[WC_SLHDSA_MAX_PUB_LEN];
        word32 rawPubLen = (word32)sizeof(rawPub);

        /* withAlg=1: full SubjectPublicKeyInfo (used by certificate code). */
        ExpectIntGT(querySpki = wc_SlhDsaKey_PublicKeyToDer(&keyGen, NULL, 0,
            1), 0);
        ExpectIntGT(derLen = (word32)wc_SlhDsaKey_PublicKeyToDer(&keyGen,
            derBuf, derBufSz, 1), 0);
        ExpectIntEQ((int)derLen, querySpki);
        ExpectIntEQ(wc_SlhDsaKey_PublicKeyToDer(&keyGen, derBuf,
            (word32)(querySpki - 1), 1), WC_NO_ERR_TRACE(BUFFER_E));

        /* withAlg=0: raw 2*n public-key bytes only -- this is the path
         * SetKeyIdFromPublicKey in asn.c walks when computing SKID/AKID
         * for SLH-DSA certificates. Verify the bytes match what
         * ExportPublic produces so a regression in this branch (e.g.
         * accidentally emitting the SPKI envelope, or returning the
         * wrong length) breaks the test rather than silently corrupting
         * key identifiers in issued certs. */
        ExpectIntGT(queryRaw = wc_SlhDsaKey_PublicKeyToDer(&keyGen, NULL, 0,
            0), 0);
        ExpectIntEQ(queryRaw, (int)(2 * keyGen.params->n));
        ExpectIntGT(wc_SlhDsaKey_PublicKeyToDer(&keyGen, derBuf, derBufSz, 0),
            0);
        ExpectIntEQ(wc_SlhDsaKey_PublicKeyToDer(&keyGen, derBuf,
            (word32)(queryRaw - 1), 0), WC_NO_ERR_TRACE(BUFFER_E));

        ExpectIntEQ(wc_SlhDsaKey_ExportPublic(&keyGen, rawPub, &rawPubLen), 0);
        ExpectIntEQ((int)rawPubLen, queryRaw);
        ExpectIntEQ(XMEMCMP(derBuf, rawPub, rawPubLen), 0);

        /* Re-encode the SPKI so the decode test below sees the
         * withAlg=1 buffer (raw output above is not decodable as SPKI). */
        ExpectIntGT(derLen = (word32)wc_SlhDsaKey_PublicKeyToDer(&keyGen,
            derBuf, derBufSz, 1), 0);
    }

    ExpectIntEQ(wc_SlhDsaKey_Init(&keyPub, placeholder, NULL, INVALID_DEVID),
        0);
    idx = 0;
    ExpectIntEQ(wc_SlhDsaKey_PublicKeyDecode(derBuf, &idx, &keyPub, derLen), 0);
    if (keyPub.params != NULL) {
        ExpectIntEQ((int)keyPub.params->param, (int)param);
    }
    /* The decoded public key should verify the signature we just produced. */
    ExpectIntEQ(wc_SlhDsaKey_Verify(&keyPub, NULL, 0, msg, (word32)sizeof(msg),
        sig, sigLen), 0);

    wc_SlhDsaKey_Free(&keyPub);
    wc_SlhDsaKey_Free(&keyPriv);
    wc_SlhDsaKey_Free(&keyGen);
    wc_FreeRng(&rng);
    XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    XFREE(derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);

    return EXPECT_RESULT();
}
#endif

/*
 * DER codec round-trip test: encode each compiled-in SLH-DSA parameter set
 * to DER, decode it (without telling the decoder which parameter set it is),
 * confirm auto-detect produces the right parameter, and verify a signature
 * produced with the decoded key. This test would fail if PrivateKeyDecode
 * / PublicKeyDecode did not auto-detect the parameter set from the OID.
 */
int test_wc_slhdsa_der_roundtrip(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
    defined(WC_ENABLE_ASYM_KEY_EXPORT)
#ifdef WOLFSSL_SLHDSA_PARAM_128S
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHAKE128S), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHAKE128F), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHAKE192S), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHAKE192F), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHAKE256S), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHAKE256F), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_SHA2
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128S
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHA2_128S), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128F
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHA2_128F), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192S
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHA2_192S), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192F
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHA2_192F), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256S
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHA2_256S), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256F
    ExpectIntEQ(slhdsa_der_roundtrip_one(SLHDSA_SHA2_256F), TEST_SUCCESS);
#endif
#endif /* WOLFSSL_SLHDSA_SHA2 */
#endif /* WOLFSSL_HAVE_SLHDSA && !VERIFY_ONLY && WC_ENABLE_ASYM_KEY_EXPORT */
    return EXPECT_RESULT();
}

/*
 * Negative / error-path tests for the DER encode/decode functions.
 */
int test_wc_slhdsa_der_negative(void)
{
    EXPECT_DECLS;
#ifdef WOLFSSL_HAVE_SLHDSA
    SlhDsaKey key;
    word32 idx;
    byte buf[16];

    XMEMSET(&key, 0, sizeof(key));

    /* PrivateKeyDecode: NULL parameters */
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
    idx = 0;
    ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(NULL, &idx, &key, 10),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(buf, NULL, &key, 10),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(buf, &idx, NULL, 10),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(buf, &idx, &key, 0),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    /* PrivateKeyDecode: truncated data */
    idx = 0;
    XMEMSET(buf, 0, sizeof(buf));
    ExpectIntNE(wc_SlhDsaKey_PrivateKeyDecode(buf, &idx, &key, sizeof(buf)), 0);
#endif

    /* PublicKeyDecode: NULL parameters */
    idx = 0;
    ExpectIntEQ(wc_SlhDsaKey_PublicKeyDecode(NULL, &idx, &key, 10),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_PublicKeyDecode(buf, NULL, &key, 10),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_PublicKeyDecode(buf, &idx, NULL, 10),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_PublicKeyDecode(buf, &idx, &key, 0),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));

#if defined(WC_ENABLE_ASYM_KEY_EXPORT) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    /* KeyToDer / PrivateKeyToDer: NULL key */
    ExpectIntEQ(wc_SlhDsaKey_KeyToDer(NULL, buf, sizeof(buf)),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
    ExpectIntEQ(wc_SlhDsaKey_PrivateKeyToDer(NULL, buf, sizeof(buf)),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));

    /* KeyToDer: public-only key should return MISSING_KEY. Build the
     * public-only state through the public API (generate a key, export
     * the public part, import it into a fresh key) rather than poking
     * key->flags directly. */
#ifdef WOLFSSL_SLHDSA_PARAM_128S
    {
        SlhDsaKey srcKey;
        SlhDsaKey pubOnly;
        WC_RNG rng;
        byte pub[WC_SLHDSA_MAX_PUB_LEN];
        word32 pubLen = (word32)sizeof(pub);

        XMEMSET(&srcKey, 0, sizeof(srcKey));
        XMEMSET(&pubOnly, 0, sizeof(pubOnly));
        XMEMSET(&rng, 0, sizeof(rng));

        ExpectIntEQ(wc_InitRng(&rng), 0);
        ExpectIntEQ(wc_SlhDsaKey_Init(&srcKey, SLHDSA_SHAKE128S, NULL,
            INVALID_DEVID), 0);
        ExpectIntEQ(wc_SlhDsaKey_MakeKey(&srcKey, &rng), 0);
        ExpectIntEQ(wc_SlhDsaKey_ExportPublic(&srcKey, pub, &pubLen), 0);

        ExpectIntEQ(wc_SlhDsaKey_Init(&pubOnly, SLHDSA_SHAKE128S, NULL,
            INVALID_DEVID), 0);
        ExpectIntEQ(wc_SlhDsaKey_ImportPublic(&pubOnly, pub, pubLen), 0);
        ExpectIntEQ(wc_SlhDsaKey_KeyToDer(&pubOnly, NULL, 0),
            WC_NO_ERR_TRACE(MISSING_KEY));
        ExpectIntEQ(wc_SlhDsaKey_PrivateKeyToDer(&pubOnly, NULL, 0),
            WC_NO_ERR_TRACE(MISSING_KEY));
        wc_SlhDsaKey_Free(&pubOnly);
        wc_SlhDsaKey_Free(&srcKey);
        wc_FreeRng(&rng);
    }
#endif
#endif /* WC_ENABLE_ASYM_KEY_EXPORT && !VERIFY_ONLY */

    /* PublicKeyToDer: NULL key */
#ifdef WC_ENABLE_ASYM_KEY_EXPORT
    ExpectIntEQ(wc_SlhDsaKey_PublicKeyToDer(NULL, buf, sizeof(buf), 1),
        WC_NO_ERR_TRACE(BAD_FUNC_ARG));
#endif

    /* RFC 5958 OneAsymmetricKey trailing-field validation:
     *   [0] IMPLICIT Attributes  OPTIONAL  -- at most once
     *   [1] IMPLICIT PublicKey   OPTIONAL  -- at most once, after [0]
     * The decoder must reject duplicates, out-of-order tags, and
     * unrecognised tags. Build a valid SHAKE128S DER then mutate it. */
#if defined(WC_ENABLE_ASYM_KEY_EXPORT) && \
    !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
    defined(WOLFSSL_SLHDSA_PARAM_128S)
    {
        SlhDsaKey srcKey;
        WC_RNG rng;
        byte goodDer[256];
        int goodLen = 0;
        size_t i;
        struct {
            const byte* trailing;
            word32      trailingLen;
            int         expectAccept; /* 0 = expect ASN_PARSE_E */
            const char* desc;
        } cases[5];
        const byte tDupAttr[]   = { 0xA0, 0x00, 0xA0, 0x00 };
        const byte tDupPub[]    = { 0xA1, 0x00, 0xA1, 0x00 };
        const byte tOutOfOrder[]= { 0xA1, 0x00, 0xA0, 0x00 };
        const byte tUnknown[]   = { 0xA2, 0x00 };
        const byte tValidAttr[] = { 0xA0, 0x00 };

        cases[0].trailing = tDupAttr;
        cases[0].trailingLen = (word32)sizeof(tDupAttr);
        cases[0].expectAccept = 0;
        cases[0].desc = "duplicate [0] attributes";
        cases[1].trailing = tDupPub;
        cases[1].trailingLen = (word32)sizeof(tDupPub);
        cases[1].expectAccept = 0;
        cases[1].desc = "duplicate [1] publicKey";
        cases[2].trailing = tOutOfOrder;
        cases[2].trailingLen = (word32)sizeof(tOutOfOrder);
        cases[2].expectAccept = 0;
        cases[2].desc = "[1] before [0]";
        cases[3].trailing = tUnknown;
        cases[3].trailingLen = (word32)sizeof(tUnknown);
        cases[3].expectAccept = 0;
        cases[3].desc = "unknown context tag [2]";
        /* Sanity: a single [0] is permitted -- if this rejects, the
         * tightening above is overzealous and the four rejection cases
         * are testing nothing useful. */
        cases[4].trailing = tValidAttr;
        cases[4].trailingLen = (word32)sizeof(tValidAttr);
        cases[4].expectAccept = 1;
        cases[4].desc = "single [0] attributes (accepted)";

        XMEMSET(&srcKey, 0, sizeof(srcKey));
        XMEMSET(&rng, 0, sizeof(rng));
        ExpectIntEQ(wc_InitRng(&rng), 0);
        ExpectIntEQ(wc_SlhDsaKey_Init(&srcKey, SLHDSA_SHAKE128S, NULL,
            INVALID_DEVID), 0);
        ExpectIntEQ(wc_SlhDsaKey_MakeKey(&srcKey, &rng), 0);
        ExpectIntGT(goodLen = wc_SlhDsaKey_KeyToDer(&srcKey, goodDer,
            sizeof(goodDer)), 0);

        /* The mutator below tweaks goodDer[1] (length byte) in place,
         * which only works if the encoder used short-form SEQUENCE
         * length. SHAKE128S body is ~82 bytes so this holds, but assert
         * it so a future encoder change surfaces here rather than
         * silently producing buffers that decode despite the mutation. */
        ExpectIntEQ((int)goodDer[0], 0x30);
        ExpectIntLT((int)goodDer[1], 0x80);
        ExpectIntLT((int)goodDer[1] + 4, 0x80);

        for (i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) {
            byte mut[260];
            word32 mutLen;
            word32 idx2 = 0;
            SlhDsaKey k;
            int decRet;
            XMEMSET(&k, 0, sizeof(k));
            XMEMCPY(mut, goodDer, (size_t)goodLen);
            XMEMCPY(mut + goodLen, cases[i].trailing, cases[i].trailingLen);
            mutLen = (word32)goodLen + cases[i].trailingLen;
            mut[1] = (byte)(goodDer[1] + cases[i].trailingLen);
            ExpectIntEQ(wc_SlhDsaKey_Init(&k, SLHDSA_SHAKE128S, NULL,
                INVALID_DEVID), 0);
            decRet = wc_SlhDsaKey_PrivateKeyDecode(mut, &idx2, &k, mutLen);
            if (cases[i].expectAccept) {
                ExpectIntEQ(decRet, 0);
            }
            else {
                ExpectIntEQ(decRet, WC_NO_ERR_TRACE(ASN_PARSE_E));
            }
            (void)cases[i].desc;
            wc_SlhDsaKey_Free(&k);
        }

        wc_SlhDsaKey_Free(&srcKey);
        wc_FreeRng(&rng);
    }
#endif /* WC_ENABLE_ASYM_KEY_EXPORT && !VERIFY_ONLY && PARAM_128S */

#endif /* WOLFSSL_HAVE_SLHDSA */
    return EXPECT_RESULT();
}

#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
    !defined(NO_FILESYSTEM)
/* Load an RFC 9909 compliant DER file from disk and confirm that
 * wc_SlhDsaKey_PrivateKeyDecode accepts it, auto-detects the parameter
 * set from the OID, and produces a usable signing key. This test
 * exercises the on-disk certs/slhdsa/ fixtures - any future file-format
 * drift (nested wrapper, seed-only, wrong length) will be caught here. */
/* doSign=0 skips the sign/verify smoke check; the 192s and 256s parameter
 * sets are slow enough (multi-second per Sign) that running them on every
 * make-check would balloon test time. The decoder is still exercised. */
static int slhdsa_decode_file_one(const char *path, enum SlhDsaParam expected,
    int doSign)
{
    EXPECT_DECLS;
    XFILE f = XBADFILE;
    byte der[256];
    int derSz = 0;
    SlhDsaKey key;
    WC_RNG rng;
    word32 idx = 0;
    byte sig[WC_SLHDSA_MAX_SIG_LEN];
    word32 sigLen = (word32)sizeof(sig);
    static const byte msg[] = "slhdsa decode-file test";

    XMEMSET(&key, 0, sizeof(key));
    XMEMSET(&rng, 0, sizeof(rng));

    ExpectTrue((f = XFOPEN(path, "rb")) != XBADFILE);
    if (f != XBADFILE) {
        ExpectIntGT(derSz = (int)XFREAD(der, 1, sizeof(der), f), 0);
        XFCLOSE(f);
    }

    /* Pick a seed param different from `expected` when more than one
     * variant is built. The decoder always overwrites this from the OID
     * in the DER, so a different placeholder actually tests the auto-
     * detect contract on disk-loaded fixtures (mirroring the helper
     * logic in slhdsa_der_roundtrip_one). Falls back to `expected` when
     * no alternative variant is available. */
    {
        static const enum SlhDsaParam candidates[] = {
            SLHDSA_SHAKE128S, SLHDSA_SHAKE128F, SLHDSA_SHAKE192S,
            SLHDSA_SHAKE192F, SLHDSA_SHAKE256S, SLHDSA_SHAKE256F,
        #ifdef WOLFSSL_SLHDSA_SHA2
            SLHDSA_SHA2_128S, SLHDSA_SHA2_128F, SLHDSA_SHA2_192S,
            SLHDSA_SHA2_192F, SLHDSA_SHA2_256S, SLHDSA_SHA2_256F,
        #endif
        };
        enum SlhDsaParam placeholder = expected;
        size_t cIdx;
        for (cIdx = 0; cIdx < sizeof(candidates)/sizeof(candidates[0]);
             cIdx++) {
            SlhDsaKey probe;
            if (candidates[cIdx] == expected) continue;
            XMEMSET(&probe, 0, sizeof(probe));
            if (wc_SlhDsaKey_Init(&probe, candidates[cIdx], NULL,
                                  INVALID_DEVID) == 0) {
                placeholder = candidates[cIdx];
                wc_SlhDsaKey_Free(&probe);
                break;
            }
        }
        ExpectIntEQ(wc_SlhDsaKey_Init(&key, placeholder, NULL, INVALID_DEVID),
            0);
    }

    ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(der, &idx, &key, (word32)derSz),
        0);
    ExpectNotNull(key.params);
    if (key.params != NULL) {
        ExpectIntEQ((int)key.params->param, (int)expected);
    }

    if (doSign) {
        /* Sanity: signing works with the decoded key. */
        ExpectIntEQ(wc_InitRng(&rng), 0);
        ExpectIntEQ(wc_SlhDsaKey_Sign(&key, NULL, 0, msg, (word32)sizeof(msg),
            sig, &sigLen, &rng), 0);
        ExpectIntEQ(wc_SlhDsaKey_Verify(&key, NULL, 0, msg,
            (word32)sizeof(msg), sig, sigLen), 0);
        wc_FreeRng(&rng);
    }
    else {
        /* Cheap structural validation when the full sign/verify is
         * skipped (slow 192s/256s variants): catches an uninitialised SK
         * half or a botched SHA-2 precompute without paying the
         * multi-second cost of an actual Sign. */
        ExpectIntEQ(wc_SlhDsaKey_CheckKey(&key), 0);
    }

    wc_SlhDsaKey_Free(&key);
    return EXPECT_RESULT();
}
#endif

/* Load each checked-in bench_slhdsa_shake*_key.der fixture and confirm it
 * decodes via wc_SlhDsaKey_PrivateKeyDecode with correct auto-detection.
 * These fixtures are RFC 9909 compliant (bare OCTET STRING, 4*n bytes) -
 * this test would fail if the files drift to a non-compliant encoding
 * (e.g. nested OCTET STRING, seed-only). */
int test_wc_slhdsa_der_decode_files(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
    !defined(NO_FILESYSTEM)
#ifdef WOLFSSL_SLHDSA_PARAM_128S
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_shake128s_key.der", SLHDSA_SHAKE128S, 1),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_shake128f_key.der", SLHDSA_SHAKE128F, 1),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_shake192s_key.der", SLHDSA_SHAKE192S, 0),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_shake192f_key.der", SLHDSA_SHAKE192F, 1),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_shake256s_key.der", SLHDSA_SHAKE256S, 0),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_shake256f_key.der", SLHDSA_SHAKE256F, 1),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_SHA2
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128S
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_sha2_128s_key.der", SLHDSA_SHA2_128S, 1),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128F
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_sha2_128f_key.der", SLHDSA_SHA2_128F, 1),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192S
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_sha2_192s_key.der", SLHDSA_SHA2_192S, 0),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192F
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_sha2_192f_key.der", SLHDSA_SHA2_192F, 1),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256S
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_sha2_256s_key.der", SLHDSA_SHA2_256S, 0),
        TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256F
    ExpectIntEQ(slhdsa_decode_file_one(
        "./certs/slhdsa/bench_slhdsa_sha2_256f_key.der", SLHDSA_SHA2_256F, 1),
        TEST_SUCCESS);
#endif
#endif /* WOLFSSL_SLHDSA_SHA2 */
#endif
    return EXPECT_RESULT();
}

#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
/* Regression: wolfssl_x509_make_der and ConfirmSignature both pass the
 * raw 2*n public-key bytes (the BIT STRING contents stashed by StoreKey
 * into cert->publicKey) to wc_SlhDsaKey_PublicKeyDecode. Before the
 * raw-first fast path landed, that call returned ASN_PARSE_E because
 * DecodeAsymKeyPublic_Assign requires an SPKI SEQUENCE. This test pins
 * the new contract: when key->params is already set, raw bytes decode
 * directly, mirroring wc_Falcon_PublicKeyDecode and
 * wc_Dilithium_PublicKeyDecode. */
static int slhdsa_raw_public_decode_one(enum SlhDsaParam param)
{
    EXPECT_DECLS;
    SlhDsaKey src;
    SlhDsaKey dst;
    byte pub[WC_SLHDSA_MAX_PUB_LEN];
    word32 pubLen = (word32)sizeof(pub);
    word32 idx = 0;
    WC_RNG rng;

    XMEMSET(&src, 0, sizeof(src));
    XMEMSET(&dst, 0, sizeof(dst));
    XMEMSET(&rng, 0, sizeof(rng));

    ExpectIntEQ(wc_InitRng(&rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_Init(&src, param, NULL, INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&src, &rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_ExportPublic(&src, pub, &pubLen), 0);

    /* Decode the raw public-key bytes via PublicKeyDecode. The fast
     * path triggers because key->params is set by Init. */
    ExpectIntEQ(wc_SlhDsaKey_Init(&dst, param, NULL, INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_PublicKeyDecode(pub, &idx, &dst, pubLen), 0);
    ExpectIntEQ((int)idx, (int)pubLen);
    ExpectNotNull(dst.params);
    if (dst.params != NULL) {
        ExpectIntEQ((int)dst.params->param, (int)param);
    }

    wc_SlhDsaKey_Free(&dst);
    wc_SlhDsaKey_Free(&src);
    wc_FreeRng(&rng);
    return EXPECT_RESULT();
}
#endif /* WOLFSSL_HAVE_SLHDSA && !WOLFSSL_SLHDSA_VERIFY_ONLY */

int test_wc_slhdsa_x509_i2d_roundtrip(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    /* Exercise the raw public-key fast path for every compiled-in variant
     * so a regression in n-dependent buffer math (32/48/64-byte keys)
     * fails the test even in restricted builds where SHAKE128S /
     * SHA2_128S happen to be excluded. */
#ifdef WOLFSSL_SLHDSA_PARAM_128S
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHAKE128S), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHAKE128F), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHAKE192S), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHAKE192F), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHAKE256S), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHAKE256F), TEST_SUCCESS);
#endif
#ifdef WOLFSSL_SLHDSA_SHA2
    #ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128S
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHA2_128S), TEST_SUCCESS);
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128F
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHA2_128F), TEST_SUCCESS);
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192S
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHA2_192S), TEST_SUCCESS);
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192F
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHA2_192F), TEST_SUCCESS);
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256S
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHA2_256S), TEST_SUCCESS);
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256F
    ExpectIntEQ(slhdsa_raw_public_decode_one(SLHDSA_SHA2_256F), TEST_SUCCESS);
    #endif
#endif /* WOLFSSL_SLHDSA_SHA2 */
#endif
    return EXPECT_RESULT();
}

#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
    defined(WC_ENABLE_ASYM_KEY_EXPORT)
/* Build a DER blob using an enabled SLH-DSA variant and patch the
 * AlgorithmIdentifier OID's trailing byte to point at a *disabled*
 * variant, then push it through the decoders. The contract being
 * pinned is that wc_SlhDsaKey_PrivateKeyDecode / PublicKeyDecode pass
 * the wc_SlhDsaOidToParam NOT_COMPILED_IN result through verbatim
 * rather than collapsing it to ASN_PARSE_E -- if that breaks, x509 /
 * TLS lose the precise "variant unavailable" diagnostic and silently
 * report malformed-DER instead.
 *
 * All SLH-DSA OIDs share the prefix 2.16.840.1.101.3.4.3.X with X<128,
 * so DER encoding lengths match exactly and the trailing byte is the
 * sole discriminator -- patching it in place produces a structurally
 * valid SPKI/PKCS#8 buffer that only fails at the OID-lookup step.
 *
 * @param src             Enabled parameter set used to generate real DER.
 * @param targetOidByte   Trailing OID byte of the disabled variant.
 */
/* Marked WC_MAYBE_UNUSED because every call site below is gated on a
 * per-variant disable macro -- builds that leave every variant enabled
 * (e.g. --enable-fips=ready) preprocess all callers away. */
static WC_MAYBE_UNUSED int slhdsa_decode_disabled_oid_one(enum SlhDsaParam src,
    byte targetOidByte)
{
    EXPECT_DECLS;
    /* OID prefix common to every SLH-DSA variant. */
    static const byte oidPrefix[] = {
        0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03
    };
    SlhDsaKey srcKey;
    SlhDsaKey dstKey;
    WC_RNG rng;
    byte* derBuf = NULL;
    const word32 derBufSz = 16 * 1024;
    int derLen = 0;
    word32 idx;
    word32 j;
    int patched;

    XMEMSET(&srcKey, 0, sizeof(srcKey));
    XMEMSET(&dstKey, 0, sizeof(dstKey));
    XMEMSET(&rng, 0, sizeof(rng));

    derBuf = (byte*)XMALLOC(derBufSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(derBuf);
    ExpectIntEQ(wc_InitRng(&rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_Init(&srcKey, src, NULL, INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&srcKey, &rng), 0);

    /* Public-key path: build SPKI, patch the variant byte, decode. */
    ExpectIntGT(derLen = wc_SlhDsaKey_PublicKeyToDer(&srcKey, derBuf, derBufSz,
        1), 0);
    patched = 0;
    for (j = 0; derLen > (int)sizeof(oidPrefix) &&
            j + sizeof(oidPrefix) < (word32)derLen; j++) {
        if (XMEMCMP(derBuf + j, oidPrefix, sizeof(oidPrefix)) == 0) {
            derBuf[j + sizeof(oidPrefix)] = targetOidByte;
            patched = 1;
            break;
        }
    }
    ExpectIntEQ(patched, 1);
    /* dstKey is zeroed (no params) so the raw fast path is skipped and
     * SPKI parsing surfaces the OID-lookup result. */
    idx = 0;
    ExpectIntEQ(wc_SlhDsaKey_PublicKeyDecode(derBuf, &idx, &dstKey,
        (word32)derLen), WC_NO_ERR_TRACE(NOT_COMPILED_IN));

    /* Private-key path: same scheme using the PKCS#8 wrapper. */
    ExpectIntGT(derLen = wc_SlhDsaKey_KeyToDer(&srcKey, derBuf, derBufSz), 0);
    patched = 0;
    for (j = 0; derLen > (int)sizeof(oidPrefix) &&
            j + sizeof(oidPrefix) < (word32)derLen; j++) {
        if (XMEMCMP(derBuf + j, oidPrefix, sizeof(oidPrefix)) == 0) {
            derBuf[j + sizeof(oidPrefix)] = targetOidByte;
            patched = 1;
            break;
        }
    }
    ExpectIntEQ(patched, 1);
    /* Free any state PublicKeyDecode may have established before the
     * second decode call reuses the key slot. */
    wc_SlhDsaKey_Free(&dstKey);
    XMEMSET(&dstKey, 0, sizeof(dstKey));
    idx = 0;
    ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(derBuf, &idx, &dstKey,
        (word32)derLen), WC_NO_ERR_TRACE(NOT_COMPILED_IN));

    wc_SlhDsaKey_Free(&dstKey);
    wc_SlhDsaKey_Free(&srcKey);
    wc_FreeRng(&rng);
    XFREE(derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    return EXPECT_RESULT();
}

/* Round-trip variant: same patcher path but the targetOidByte must equal
 * the source's own OID byte, so the decode is expected to succeed. Used
 * as an unconditional smoke check so test_wc_slhdsa_decoder_disabled_oid
 * exercises the patcher infrastructure even on builds with no per-variant
 * disable, where every disabled-branch in the caller is #if'd out. */
static int slhdsa_decode_disabled_oid_one_roundtrip(enum SlhDsaParam src,
    byte targetOidByte)
{
    EXPECT_DECLS;
    static const byte oidPrefix[] = {
        0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03
    };
    SlhDsaKey srcKey;
    SlhDsaKey dstKey;
    WC_RNG rng;
    byte* derBuf = NULL;
    const word32 derBufSz = 16 * 1024;
    int derLen = 0;
    word32 idx;
    word32 j;
    int patched;

    XMEMSET(&srcKey, 0, sizeof(srcKey));
    XMEMSET(&dstKey, 0, sizeof(dstKey));
    XMEMSET(&rng, 0, sizeof(rng));

    derBuf = (byte*)XMALLOC(derBufSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(derBuf);
    ExpectIntEQ(wc_InitRng(&rng), 0);
    ExpectIntEQ(wc_SlhDsaKey_Init(&srcKey, src, NULL, INVALID_DEVID), 0);
    ExpectIntEQ(wc_SlhDsaKey_MakeKey(&srcKey, &rng), 0);

    ExpectIntGT(derLen = wc_SlhDsaKey_PublicKeyToDer(&srcKey, derBuf, derBufSz,
        1), 0);
    patched = 0;
    for (j = 0; derLen > (int)sizeof(oidPrefix) &&
            j + sizeof(oidPrefix) < (word32)derLen; j++) {
        if (XMEMCMP(derBuf + j, oidPrefix, sizeof(oidPrefix)) == 0) {
            derBuf[j + sizeof(oidPrefix)] = targetOidByte;
            patched = 1;
            break;
        }
    }
    ExpectIntEQ(patched, 1);
    idx = 0;
    ExpectIntEQ(wc_SlhDsaKey_PublicKeyDecode(derBuf, &idx, &dstKey,
        (word32)derLen), 0);

    wc_SlhDsaKey_Free(&dstKey);
    wc_SlhDsaKey_Free(&srcKey);
    wc_FreeRng(&rng);
    XFREE(derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    return EXPECT_RESULT();
}

/* Probe candidate parameter sets in priority order and return the first
 * one whose backend is built in. Used to source a real DER buffer for
 * slhdsa_decode_disabled_oid_one regardless of which variants the
 * current build excluded. Returns 1 on success, 0 if no variant is
 * available (which the build's #error guard makes impossible in
 * practice but the test handles defensively). */
static int slhdsa_pick_enabled_param(enum SlhDsaParam* out)
{
    static const enum SlhDsaParam candidates[] = {
        SLHDSA_SHAKE128S, SLHDSA_SHAKE128F, SLHDSA_SHAKE192S,
        SLHDSA_SHAKE192F, SLHDSA_SHAKE256S, SLHDSA_SHAKE256F,
    #ifdef WOLFSSL_SLHDSA_SHA2
        SLHDSA_SHA2_128S, SLHDSA_SHA2_128F, SLHDSA_SHA2_192S,
        SLHDSA_SHA2_192F, SLHDSA_SHA2_256S, SLHDSA_SHA2_256F,
    #endif
    };
    size_t i;

    for (i = 0; i < sizeof(candidates)/sizeof(candidates[0]); i++) {
        SlhDsaKey probe;
        XMEMSET(&probe, 0, sizeof(probe));
        if (wc_SlhDsaKey_Init(&probe, candidates[i], NULL,
                              INVALID_DEVID) == 0) {
            wc_SlhDsaKey_Free(&probe);
            *out = candidates[i];
            return 1;
        }
    }
    return 0;
}
#endif /* WOLFSSL_HAVE_SLHDSA && !VERIFY_ONLY && WC_ENABLE_ASYM_KEY_EXPORT */

/* Pin the per-variant disable contract: every parameter set whose enum
 * value is visible (so the test compiles) but whose backend is excluded
 * by a WOLFSSL_SLHDSA_PARAM_NO_* / WOLFSSL_SLHDSA_NO_* macro must surface
 * NOT_COMPILED_IN from wc_SlhDsaKey_Init instead of silently succeeding
 * or returning a generic error.
 *
 * This is the only API-level test for the granular disable surface and
 * also locks in the contract that wc_SlhDsaOidToParam/CertType in asn.c
 * piggyback on -- if Init drifts away from NOT_COMPILED_IN here, the
 * mapping helpers will likewise diverge and certificate handling will
 * lose its "variant unavailable" diagnostic. */
int test_wc_slhdsa_param_disabled(void)
{
    EXPECT_DECLS;
#ifdef WOLFSSL_HAVE_SLHDSA
    SlhDsaKey key;
    enum SlhDsaParam enabledProbe = SLHDSA_SHAKE128S;
    int haveEnabled;

    XMEMSET(&key, 0, sizeof(key));

    /* Positive smoke check: at least one variant must initialise. Without
     * this the disabled-variant branches below can all be #if'd out and
     * the test would silently pass on default builds, defeating its
     * documented purpose. The probe also validates the contract from the
     * other side -- Init must succeed for an enabled param. */
    haveEnabled = 0;
#if defined(WOLFSSL_SLHDSA_PARAM_128S)
    enabledProbe = SLHDSA_SHAKE128S; haveEnabled = 1;
#elif defined(WOLFSSL_SLHDSA_PARAM_128F)
    enabledProbe = SLHDSA_SHAKE128F; haveEnabled = 1;
#elif defined(WOLFSSL_SLHDSA_PARAM_192S)
    enabledProbe = SLHDSA_SHAKE192S; haveEnabled = 1;
#elif defined(WOLFSSL_SLHDSA_PARAM_192F)
    enabledProbe = SLHDSA_SHAKE192F; haveEnabled = 1;
#elif defined(WOLFSSL_SLHDSA_PARAM_256S)
    enabledProbe = SLHDSA_SHAKE256S; haveEnabled = 1;
#elif defined(WOLFSSL_SLHDSA_PARAM_256F)
    enabledProbe = SLHDSA_SHAKE256F; haveEnabled = 1;
#elif defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
    enabledProbe = SLHDSA_SHA2_128S; haveEnabled = 1;
#endif
    ExpectIntEQ(haveEnabled, 1);
    if (haveEnabled) {
        ExpectIntEQ(wc_SlhDsaKey_Init(&key, enabledProbe, NULL,
            INVALID_DEVID), 0);
        wc_SlhDsaKey_Free(&key);
        XMEMSET(&key, 0, sizeof(key));
    }

#if defined(WOLFSSL_SLHDSA_PARAM_NO_128S) || \
    defined(WOLFSSL_SLHDSA_NO_128) || \
    defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128S, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_128F) || \
    defined(WOLFSSL_SLHDSA_NO_128) || \
    defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128F, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_192S) || \
    defined(WOLFSSL_SLHDSA_NO_192) || \
    defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192S, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_192F) || \
    defined(WOLFSSL_SLHDSA_NO_192) || \
    defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE192F, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_256S) || \
    defined(WOLFSSL_SLHDSA_NO_256) || \
    defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256S, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_256F) || \
    defined(WOLFSSL_SLHDSA_NO_256) || \
    defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE256F, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
#endif

#ifdef WOLFSSL_SLHDSA_SHA2
    /* SHA-2 enum values are only declared when WOLFSSL_SLHDSA_SHA2 is set;
     * each per-variant disable below is checked under that gate. */
    #ifdef WOLFSSL_SLHDSA_PARAM_NO_SHA2_128S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128S, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_NO_SHA2_128F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128F, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_NO_SHA2_192S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192S, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_NO_SHA2_192F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192F, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_NO_SHA2_256S
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256S, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
    #endif
    #ifdef WOLFSSL_SLHDSA_PARAM_NO_SHA2_256F
    ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_256F, NULL, INVALID_DEVID),
        WC_NO_ERR_TRACE(NOT_COMPILED_IN));
    #endif
#endif /* WOLFSSL_SLHDSA_SHA2 */

    (void)key;
#endif /* WOLFSSL_HAVE_SLHDSA */
    return EXPECT_RESULT();
}

/* Decoder-level companion to test_wc_slhdsa_param_disabled: feed DER for
 * each disabled SLH-DSA OID into PrivateKeyDecode/PublicKeyDecode and
 * confirm they pass NOT_COMPILED_IN through verbatim. The Init-level
 * test above proves the mapping helper is correct; this one proves the
 * decoders honour it instead of collapsing the result to ASN_PARSE_E. */
int test_wc_slhdsa_decoder_disabled_oid(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
    defined(WC_ENABLE_ASYM_KEY_EXPORT)
    enum SlhDsaParam src = SLHDSA_SHAKE128S;
    int haveSrc = slhdsa_pick_enabled_param(&src);
    ExpectIntEQ(haveSrc, 1);

    if (haveSrc) {
        /* Positive smoke check: feed src's own OID through the patcher
         * (round-trip) and expect a clean decode, so the decoder path
         * actually exercises here even when no per-variant disable is
         * active. The trailing OID byte for SHAKE128S is 0x1A; we look
         * up the byte for `src` from a small table because src may not
         * be SHAKE128S in restricted builds. */
        {
            byte srcOidByte = 0;
            switch (src) {
                case SLHDSA_SHAKE128S: srcOidByte = 0x1A; break;
                case SLHDSA_SHAKE128F: srcOidByte = 0x1B; break;
                case SLHDSA_SHAKE192S: srcOidByte = 0x1C; break;
                case SLHDSA_SHAKE192F: srcOidByte = 0x1D; break;
                case SLHDSA_SHAKE256S: srcOidByte = 0x1E; break;
                case SLHDSA_SHAKE256F: srcOidByte = 0x1F; break;
            #ifdef WOLFSSL_SLHDSA_SHA2
                case SLHDSA_SHA2_128S: srcOidByte = 0x14; break;
                case SLHDSA_SHA2_128F: srcOidByte = 0x15; break;
                case SLHDSA_SHA2_192S: srcOidByte = 0x16; break;
                case SLHDSA_SHA2_192F: srcOidByte = 0x17; break;
                case SLHDSA_SHA2_256S: srcOidByte = 0x18; break;
                case SLHDSA_SHA2_256F: srcOidByte = 0x19; break;
            #endif
                default: break;
            }
            ExpectIntGT((int)srcOidByte, 0);
            /* Round-trip: patching src's OID to itself must still decode
             * successfully -- this fires unconditionally and validates
             * the patcher infrastructure even when no disable branch
             * below applies. */
            ExpectIntEQ(slhdsa_decode_disabled_oid_one_roundtrip(src,
                srcOidByte), TEST_SUCCESS);
        }

        /* SHAKE family: trailing OID byte = 0x1A..0x1F. */
        #if defined(WOLFSSL_SLHDSA_PARAM_NO_128S) || \
            defined(WOLFSSL_SLHDSA_NO_128) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x1A), TEST_SUCCESS);
        #endif
        #if defined(WOLFSSL_SLHDSA_PARAM_NO_128F) || \
            defined(WOLFSSL_SLHDSA_NO_128) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x1B), TEST_SUCCESS);
        #endif
        #if defined(WOLFSSL_SLHDSA_PARAM_NO_192S) || \
            defined(WOLFSSL_SLHDSA_NO_192) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x1C), TEST_SUCCESS);
        #endif
        #if defined(WOLFSSL_SLHDSA_PARAM_NO_192F) || \
            defined(WOLFSSL_SLHDSA_NO_192) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x1D), TEST_SUCCESS);
        #endif
        #if defined(WOLFSSL_SLHDSA_PARAM_NO_256S) || \
            defined(WOLFSSL_SLHDSA_NO_256) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x1E), TEST_SUCCESS);
        #endif
        #if defined(WOLFSSL_SLHDSA_PARAM_NO_256F) || \
            defined(WOLFSSL_SLHDSA_NO_256) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x1F), TEST_SUCCESS);
        #endif

        /* SHA-2 family: trailing OID byte = 0x14..0x19. The whole
         * family is also disabled when WOLFSSL_SLHDSA_SHA2 is unset. */
        #if !defined(WOLFSSL_SLHDSA_SHA2) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128S)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x14), TEST_SUCCESS);
        #endif
        #if !defined(WOLFSSL_SLHDSA_SHA2) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128F)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x15), TEST_SUCCESS);
        #endif
        #if !defined(WOLFSSL_SLHDSA_SHA2) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192S)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x16), TEST_SUCCESS);
        #endif
        #if !defined(WOLFSSL_SLHDSA_SHA2) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192F)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x17), TEST_SUCCESS);
        #endif
        #if !defined(WOLFSSL_SLHDSA_SHA2) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256S)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x18), TEST_SUCCESS);
        #endif
        #if !defined(WOLFSSL_SLHDSA_SHA2) || \
            defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256F)
        ExpectIntEQ(slhdsa_decode_disabled_oid_one(src, 0x19), TEST_SUCCESS);
        #endif
    }
#endif
    return EXPECT_RESULT();
}

#ifdef TEST_SLHDSA_DEFAULT_PARAM
    #undef TEST_SLHDSA_DEFAULT_PARAM
    #undef TEST_SLHDSA_DEFAULT_SIG_LEN
    #undef TEST_SLHDSA_DEFAULT_PRIV_LEN
    #undef TEST_SLHDSA_DEFAULT_PUB_LEN
    #undef TEST_SLHDSA_DEFAULT_SEED_LEN
#endif
