/* dh.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 <wolfssl/wolfcrypt/libwolfssl_sources.h>

#ifndef NO_DH

#if defined(HAVE_FIPS) && \
    defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)

    /* set NO_WRAPPERS before headers, use direct internal f()s not wrappers */
    #define FIPS_NO_WRAPPERS

    #ifdef USE_WINDOWS_API
        #pragma code_seg(".fipsA$e")
        #pragma const_seg(".fipsB$e")
    #endif
#endif

#include <wolfssl/wolfcrypt/dh.h>

#ifdef WOLFSSL_HAVE_SP_DH
#include <wolfssl/wolfcrypt/sp.h>
#endif

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

#if FIPS_VERSION3_GE(6,0,0)
    const unsigned int wolfCrypt_FIPS_dh_ro_sanity[2] =
                                                     { 0x1a2b3c4d, 0x00000004 };
    int wolfCrypt_FIPS_DH_sanity(void)
    {
        return 0;
    }
#endif

#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && !defined(WOLFSSL_SP_ASM)
    /* force off unneeded vector register save/restore. */
    #undef SAVE_VECTOR_REGISTERS
    #define SAVE_VECTOR_REGISTERS(fail_clause) SAVE_NO_VECTOR_REGISTERS(fail_clause)
    #undef RESTORE_VECTOR_REGISTERS
    #define RESTORE_VECTOR_REGISTERS() RESTORE_NO_VECTOR_REGISTERS()
#endif

/*
Possible DH enable options:
 * NO_RSA:              Overall control of DH                 default: on (not defined)
 * WOLFSSL_OLD_PRIME_CHECK: Disables the new prime number check. It does not
                        directly effect this file, but it does speed up DH
                        removing the testing. It is not recommended to
                        disable the prime checking.           default: off
 * WOLFSSL_VALIDATE_DH_KEYGEN: Enable DH key gen consistency checking
 *                             (on for FIPS 140-3 or later)   default: off
*/


#if !defined(USER_MATH_LIB) && !defined(WOLFSSL_DH_CONST)
    #include <math.h>
    #define XPOW(x,y) pow((x),(y))
    #define XLOG(x)   log((x))
#else
    /* user's own math lib */
#endif

#ifdef HAVE_FFDHE_2048
static const byte dh_ffdhe2048_p[] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
    0x88, 0x6B, 0x42, 0x38, 0x61, 0x28, 0x5C, 0x97,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const byte dh_ffdhe2048_g[] = { 0x02 };
#ifdef HAVE_FFDHE_Q
static const byte dh_ffdhe2048_q[] = {
    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xD6, 0xFC, 0x2A, 0x2C, 0x51, 0x5D, 0xA5, 0x4D,
    0x57, 0xEE, 0x2B, 0x10, 0x13, 0x9E, 0x9E, 0x78,
    0xEC, 0x5C, 0xE2, 0xC1, 0xE7, 0x16, 0x9B, 0x4A,
    0xD4, 0xF0, 0x9B, 0x20, 0x8A, 0x32, 0x19, 0xFD,
    0xE6, 0x49, 0xCE, 0xE7, 0x12, 0x4D, 0x9F, 0x7C,
    0xBE, 0x97, 0xF1, 0xB1, 0xB1, 0x86, 0x3A, 0xEC,
    0x7B, 0x40, 0xD9, 0x01, 0x57, 0x62, 0x30, 0xBD,
    0x69, 0xEF, 0x8F, 0x6A, 0xEA, 0xFE, 0xB2, 0xB0,
    0x92, 0x19, 0xFA, 0x8F, 0xAF, 0x83, 0x37, 0x68,
    0x42, 0xB1, 0xB2, 0xAA, 0x9E, 0xF6, 0x8D, 0x79,
    0xDA, 0xAB, 0x89, 0xAF, 0x3F, 0xAB, 0xE4, 0x9A,
    0xCC, 0x27, 0x86, 0x38, 0x70, 0x73, 0x45, 0xBB,
    0xF1, 0x53, 0x44, 0xED, 0x79, 0xF7, 0xF4, 0x39,
    0x0E, 0xF8, 0xAC, 0x50, 0x9B, 0x56, 0xF3, 0x9A,
    0x98, 0x56, 0x65, 0x27, 0xA4, 0x1D, 0x3C, 0xBD,
    0x5E, 0x05, 0x58, 0xC1, 0x59, 0x92, 0x7D, 0xB0,
    0xE8, 0x84, 0x54, 0xA5, 0xD9, 0x64, 0x71, 0xFD,
    0xDC, 0xB5, 0x6D, 0x5B, 0xB0, 0x6B, 0xFA, 0x34,
    0x0E, 0xA7, 0xA1, 0x51, 0xEF, 0x1C, 0xA6, 0xFA,
    0x57, 0x2B, 0x76, 0xF3, 0xB1, 0xB9, 0x5D, 0x8C,
    0x85, 0x83, 0xD3, 0xE4, 0x77, 0x05, 0x36, 0xB8,
    0x4F, 0x01, 0x7E, 0x70, 0xE6, 0xFB, 0xF1, 0x76,
    0x60, 0x1A, 0x02, 0x66, 0x94, 0x1A, 0x17, 0xB0,
    0xC8, 0xB9, 0x7F, 0x4E, 0x74, 0xC2, 0xC1, 0xFF,
    0xC7, 0x27, 0x89, 0x19, 0x77, 0x79, 0x40, 0xC1,
    0xE1, 0xFF, 0x1D, 0x8D, 0xA6, 0x37, 0xD6, 0xB9,
    0x9D, 0xDA, 0xFE, 0x5E, 0x17, 0x61, 0x10, 0x02,
    0xE2, 0xC7, 0x78, 0xC1, 0xBE, 0x8B, 0x41, 0xD9,
    0x63, 0x79, 0xA5, 0x13, 0x60, 0xD9, 0x77, 0xFD,
    0x44, 0x35, 0xA1, 0x1C, 0x30, 0x94, 0x2E, 0x4B,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
#endif /* HAVE_FFDHE_Q */

#ifdef HAVE_PUBLIC_FFDHE
const DhParams* wc_Dh_ffdhe2048_Get(void)
{
    static const DhParams ffdhe2048 = {
        #ifdef HAVE_FFDHE_Q
            dh_ffdhe2048_q, sizeof(dh_ffdhe2048_q),
        #endif /* HAVE_FFDHE_Q */
        dh_ffdhe2048_p, sizeof(dh_ffdhe2048_p),
        dh_ffdhe2048_g, sizeof(dh_ffdhe2048_g)
    };
    return &ffdhe2048;
}
#endif
#endif

#ifdef HAVE_FFDHE_3072
static const byte dh_ffdhe3072_p[] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
    0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC,
    0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B,
    0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38,
    0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07,
    0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE,
    0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C,
    0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70,
    0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44,
    0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3,
    0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF,
    0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E,
    0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D,
    0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA,
    0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E,
    0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF,
    0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C,
    0x25, 0xE4, 0x1D, 0x2B, 0x66, 0xC6, 0x2E, 0x37,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const byte dh_ffdhe3072_g[] = { 0x02 };
#ifdef HAVE_FFDHE_Q
static const byte dh_ffdhe3072_q[] = {
    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xD6, 0xFC, 0x2A, 0x2C, 0x51, 0x5D, 0xA5, 0x4D,
    0x57, 0xEE, 0x2B, 0x10, 0x13, 0x9E, 0x9E, 0x78,
    0xEC, 0x5C, 0xE2, 0xC1, 0xE7, 0x16, 0x9B, 0x4A,
    0xD4, 0xF0, 0x9B, 0x20, 0x8A, 0x32, 0x19, 0xFD,
    0xE6, 0x49, 0xCE, 0xE7, 0x12, 0x4D, 0x9F, 0x7C,
    0xBE, 0x97, 0xF1, 0xB1, 0xB1, 0x86, 0x3A, 0xEC,
    0x7B, 0x40, 0xD9, 0x01, 0x57, 0x62, 0x30, 0xBD,
    0x69, 0xEF, 0x8F, 0x6A, 0xEA, 0xFE, 0xB2, 0xB0,
    0x92, 0x19, 0xFA, 0x8F, 0xAF, 0x83, 0x37, 0x68,
    0x42, 0xB1, 0xB2, 0xAA, 0x9E, 0xF6, 0x8D, 0x79,
    0xDA, 0xAB, 0x89, 0xAF, 0x3F, 0xAB, 0xE4, 0x9A,
    0xCC, 0x27, 0x86, 0x38, 0x70, 0x73, 0x45, 0xBB,
    0xF1, 0x53, 0x44, 0xED, 0x79, 0xF7, 0xF4, 0x39,
    0x0E, 0xF8, 0xAC, 0x50, 0x9B, 0x56, 0xF3, 0x9A,
    0x98, 0x56, 0x65, 0x27, 0xA4, 0x1D, 0x3C, 0xBD,
    0x5E, 0x05, 0x58, 0xC1, 0x59, 0x92, 0x7D, 0xB0,
    0xE8, 0x84, 0x54, 0xA5, 0xD9, 0x64, 0x71, 0xFD,
    0xDC, 0xB5, 0x6D, 0x5B, 0xB0, 0x6B, 0xFA, 0x34,
    0x0E, 0xA7, 0xA1, 0x51, 0xEF, 0x1C, 0xA6, 0xFA,
    0x57, 0x2B, 0x76, 0xF3, 0xB1, 0xB9, 0x5D, 0x8C,
    0x85, 0x83, 0xD3, 0xE4, 0x77, 0x05, 0x36, 0xB8,
    0x4F, 0x01, 0x7E, 0x70, 0xE6, 0xFB, 0xF1, 0x76,
    0x60, 0x1A, 0x02, 0x66, 0x94, 0x1A, 0x17, 0xB0,
    0xC8, 0xB9, 0x7F, 0x4E, 0x74, 0xC2, 0xC1, 0xFF,
    0xC7, 0x27, 0x89, 0x19, 0x77, 0x79, 0x40, 0xC1,
    0xE1, 0xFF, 0x1D, 0x8D, 0xA6, 0x37, 0xD6, 0xB9,
    0x9D, 0xDA, 0xFE, 0x5E, 0x17, 0x61, 0x10, 0x02,
    0xE2, 0xC7, 0x78, 0xC1, 0xBE, 0x8B, 0x41, 0xD9,
    0x63, 0x79, 0xA5, 0x13, 0x60, 0xD9, 0x77, 0xFD,
    0x44, 0x35, 0xA1, 0x1C, 0x30, 0x8F, 0xE7, 0xEE,
    0x6F, 0x1A, 0xAD, 0x9D, 0xB2, 0x8C, 0x81, 0xAD,
    0xDE, 0x1A, 0x7A, 0x6F, 0x7C, 0xCE, 0x01, 0x1C,
    0x30, 0xDA, 0x37, 0xE4, 0xEB, 0x73, 0x64, 0x83,
    0xBD, 0x6C, 0x8E, 0x93, 0x48, 0xFB, 0xFB, 0xF7,
    0x2C, 0xC6, 0x58, 0x7D, 0x60, 0xC3, 0x6C, 0x8E,
    0x57, 0x7F, 0x09, 0x84, 0xC2, 0x89, 0xC9, 0x38,
    0x5A, 0x09, 0x86, 0x49, 0xDE, 0x21, 0xBC, 0xA2,
    0x7A, 0x7E, 0xA2, 0x29, 0x71, 0x6B, 0xA6, 0xE9,
    0xB2, 0x79, 0x71, 0x0F, 0x38, 0xFA, 0xA5, 0xFF,
    0xAE, 0x57, 0x41, 0x55, 0xCE, 0x4E, 0xFB, 0x4F,
    0x74, 0x36, 0x95, 0xE2, 0x91, 0x1B, 0x1D, 0x06,
    0xD5, 0xE2, 0x90, 0xCB, 0xCD, 0x86, 0xF5, 0x6D,
    0x0E, 0xDF, 0xCD, 0x21, 0x6A, 0xE2, 0x24, 0x27,
    0x05, 0x5E, 0x68, 0x35, 0xFD, 0x29, 0xEE, 0xF7,
    0x9E, 0x0D, 0x90, 0x77, 0x1F, 0xEA, 0xCE, 0xBE,
    0x12, 0xF2, 0x0E, 0x95, 0xB3, 0x63, 0x17, 0x1B,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
#endif /* HAVE_FFDHE_Q */

#ifdef HAVE_PUBLIC_FFDHE
const DhParams* wc_Dh_ffdhe3072_Get(void)
{
    static const DhParams ffdhe3072 = {
        #ifdef HAVE_FFDHE_Q
            dh_ffdhe3072_q, sizeof(dh_ffdhe3072_q),
        #endif /* HAVE_FFDHE_Q */
        dh_ffdhe3072_p, sizeof(dh_ffdhe3072_p),
        dh_ffdhe3072_g, sizeof(dh_ffdhe3072_g)
    };
    return &ffdhe3072;
}
#endif
#endif

#ifdef HAVE_FFDHE_4096
static const byte dh_ffdhe4096_p[] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
    0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC,
    0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B,
    0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38,
    0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07,
    0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE,
    0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C,
    0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70,
    0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44,
    0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3,
    0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF,
    0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E,
    0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D,
    0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA,
    0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E,
    0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF,
    0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C,
    0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1,
    0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB,
    0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6,
    0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18,
    0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04,
    0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A,
    0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A,
    0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32,
    0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4,
    0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38,
    0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A,
    0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C,
    0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC,
    0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF,
    0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B,
    0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1,
    0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x65, 0x5F, 0x6A,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const byte dh_ffdhe4096_g[] = { 0x02 };
#ifdef HAVE_FFDHE_Q
static const byte dh_ffdhe4096_q[] = {
    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xD6, 0xFC, 0x2A, 0x2C, 0x51, 0x5D, 0xA5, 0x4D,
    0x57, 0xEE, 0x2B, 0x10, 0x13, 0x9E, 0x9E, 0x78,
    0xEC, 0x5C, 0xE2, 0xC1, 0xE7, 0x16, 0x9B, 0x4A,
    0xD4, 0xF0, 0x9B, 0x20, 0x8A, 0x32, 0x19, 0xFD,
    0xE6, 0x49, 0xCE, 0xE7, 0x12, 0x4D, 0x9F, 0x7C,
    0xBE, 0x97, 0xF1, 0xB1, 0xB1, 0x86, 0x3A, 0xEC,
    0x7B, 0x40, 0xD9, 0x01, 0x57, 0x62, 0x30, 0xBD,
    0x69, 0xEF, 0x8F, 0x6A, 0xEA, 0xFE, 0xB2, 0xB0,
    0x92, 0x19, 0xFA, 0x8F, 0xAF, 0x83, 0x37, 0x68,
    0x42, 0xB1, 0xB2, 0xAA, 0x9E, 0xF6, 0x8D, 0x79,
    0xDA, 0xAB, 0x89, 0xAF, 0x3F, 0xAB, 0xE4, 0x9A,
    0xCC, 0x27, 0x86, 0x38, 0x70, 0x73, 0x45, 0xBB,
    0xF1, 0x53, 0x44, 0xED, 0x79, 0xF7, 0xF4, 0x39,
    0x0E, 0xF8, 0xAC, 0x50, 0x9B, 0x56, 0xF3, 0x9A,
    0x98, 0x56, 0x65, 0x27, 0xA4, 0x1D, 0x3C, 0xBD,
    0x5E, 0x05, 0x58, 0xC1, 0x59, 0x92, 0x7D, 0xB0,
    0xE8, 0x84, 0x54, 0xA5, 0xD9, 0x64, 0x71, 0xFD,
    0xDC, 0xB5, 0x6D, 0x5B, 0xB0, 0x6B, 0xFA, 0x34,
    0x0E, 0xA7, 0xA1, 0x51, 0xEF, 0x1C, 0xA6, 0xFA,
    0x57, 0x2B, 0x76, 0xF3, 0xB1, 0xB9, 0x5D, 0x8C,
    0x85, 0x83, 0xD3, 0xE4, 0x77, 0x05, 0x36, 0xB8,
    0x4F, 0x01, 0x7E, 0x70, 0xE6, 0xFB, 0xF1, 0x76,
    0x60, 0x1A, 0x02, 0x66, 0x94, 0x1A, 0x17, 0xB0,
    0xC8, 0xB9, 0x7F, 0x4E, 0x74, 0xC2, 0xC1, 0xFF,
    0xC7, 0x27, 0x89, 0x19, 0x77, 0x79, 0x40, 0xC1,
    0xE1, 0xFF, 0x1D, 0x8D, 0xA6, 0x37, 0xD6, 0xB9,
    0x9D, 0xDA, 0xFE, 0x5E, 0x17, 0x61, 0x10, 0x02,
    0xE2, 0xC7, 0x78, 0xC1, 0xBE, 0x8B, 0x41, 0xD9,
    0x63, 0x79, 0xA5, 0x13, 0x60, 0xD9, 0x77, 0xFD,
    0x44, 0x35, 0xA1, 0x1C, 0x30, 0x8F, 0xE7, 0xEE,
    0x6F, 0x1A, 0xAD, 0x9D, 0xB2, 0x8C, 0x81, 0xAD,
    0xDE, 0x1A, 0x7A, 0x6F, 0x7C, 0xCE, 0x01, 0x1C,
    0x30, 0xDA, 0x37, 0xE4, 0xEB, 0x73, 0x64, 0x83,
    0xBD, 0x6C, 0x8E, 0x93, 0x48, 0xFB, 0xFB, 0xF7,
    0x2C, 0xC6, 0x58, 0x7D, 0x60, 0xC3, 0x6C, 0x8E,
    0x57, 0x7F, 0x09, 0x84, 0xC2, 0x89, 0xC9, 0x38,
    0x5A, 0x09, 0x86, 0x49, 0xDE, 0x21, 0xBC, 0xA2,
    0x7A, 0x7E, 0xA2, 0x29, 0x71, 0x6B, 0xA6, 0xE9,
    0xB2, 0x79, 0x71, 0x0F, 0x38, 0xFA, 0xA5, 0xFF,
    0xAE, 0x57, 0x41, 0x55, 0xCE, 0x4E, 0xFB, 0x4F,
    0x74, 0x36, 0x95, 0xE2, 0x91, 0x1B, 0x1D, 0x06,
    0xD5, 0xE2, 0x90, 0xCB, 0xCD, 0x86, 0xF5, 0x6D,
    0x0E, 0xDF, 0xCD, 0x21, 0x6A, 0xE2, 0x24, 0x27,
    0x05, 0x5E, 0x68, 0x35, 0xFD, 0x29, 0xEE, 0xF7,
    0x9E, 0x0D, 0x90, 0x77, 0x1F, 0xEA, 0xCE, 0xBE,
    0x12, 0xF2, 0x0E, 0x95, 0xB3, 0x4F, 0x0F, 0x78,
    0xB7, 0x37, 0xA9, 0x61, 0x8B, 0x26, 0xFA, 0x7D,
    0xBC, 0x98, 0x74, 0xF2, 0x72, 0xC4, 0x2B, 0xDB,
    0x56, 0x3E, 0xAF, 0xA1, 0x6B, 0x4F, 0xB6, 0x8C,
    0x3B, 0xB1, 0xE7, 0x8E, 0xAA, 0x81, 0xA0, 0x02,
    0x43, 0xFA, 0xAD, 0xD2, 0xBF, 0x18, 0xE6, 0x3D,
    0x38, 0x9A, 0xE4, 0x43, 0x77, 0xDA, 0x18, 0xC5,
    0x76, 0xB5, 0x0F, 0x00, 0x96, 0xCF, 0x34, 0x19,
    0x54, 0x83, 0xB0, 0x05, 0x48, 0xC0, 0x98, 0x62,
    0x36, 0xE3, 0xBC, 0x7C, 0xB8, 0xD6, 0x80, 0x1C,
    0x04, 0x94, 0xCC, 0xD1, 0x99, 0xE5, 0xC5, 0xBD,
    0x0D, 0x0E, 0xDC, 0x9E, 0xB8, 0xA0, 0x00, 0x1E,
    0x15, 0x27, 0x67, 0x54, 0xFC, 0xC6, 0x85, 0x66,
    0x05, 0x41, 0x48, 0xE6, 0xE7, 0x64, 0xBE, 0xE7,
    0xC7, 0x64, 0xDA, 0xAD, 0x3F, 0xC4, 0x52, 0x35,
    0xA6, 0xDA, 0xD4, 0x28, 0xFA, 0x20, 0xC1, 0x70,
    0xE3, 0x45, 0x00, 0x3F, 0x2F, 0x32, 0xAF, 0xB5,
    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
#endif /* HAVE_FFDHE_Q */

#ifdef HAVE_PUBLIC_FFDHE
const DhParams* wc_Dh_ffdhe4096_Get(void)
{
    static const DhParams ffdhe4096 = {
        #ifdef HAVE_FFDHE_Q
            dh_ffdhe4096_q, sizeof(dh_ffdhe4096_q),
        #endif /* HAVE_FFDHE_Q */
        dh_ffdhe4096_p, sizeof(dh_ffdhe4096_p),
        dh_ffdhe4096_g, sizeof(dh_ffdhe4096_g)
    };
    return &ffdhe4096;
}
#endif
#endif

#ifdef HAVE_FFDHE_6144
static const byte dh_ffdhe6144_p[] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
    0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC,
    0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B,
    0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38,
    0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07,
    0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE,
    0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C,
    0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70,
    0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44,
    0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3,
    0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF,
    0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E,
    0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D,
    0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA,
    0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E,
    0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF,
    0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C,
    0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1,
    0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB,
    0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6,
    0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18,
    0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04,
    0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A,
    0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A,
    0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32,
    0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4,
    0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38,
    0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A,
    0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C,
    0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC,
    0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF,
    0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B,
    0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1,
    0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02,
    0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A,
    0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A,
    0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6,
    0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8,
    0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C,
    0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A,
    0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71,
    0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F,
    0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77,
    0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10,
    0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8,
    0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3,
    0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E,
    0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3,
    0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4,
    0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1,
    0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92,
    0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6,
    0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82,
    0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE,
    0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C,
    0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E,
    0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46,
    0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A,
    0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17,
    0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03,
    0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04,
    0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6,
    0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69,
    0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1,
    0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4,
    0xA4, 0x0E, 0x32, 0x9C, 0xD0, 0xE4, 0x0E, 0x65,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const byte dh_ffdhe6144_g[] = { 0x02 };
#ifdef HAVE_FFDHE_Q
static const byte dh_ffdhe6144_q[] = {
    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xD6, 0xFC, 0x2A, 0x2C, 0x51, 0x5D, 0xA5, 0x4D,
    0x57, 0xEE, 0x2B, 0x10, 0x13, 0x9E, 0x9E, 0x78,
    0xEC, 0x5C, 0xE2, 0xC1, 0xE7, 0x16, 0x9B, 0x4A,
    0xD4, 0xF0, 0x9B, 0x20, 0x8A, 0x32, 0x19, 0xFD,
    0xE6, 0x49, 0xCE, 0xE7, 0x12, 0x4D, 0x9F, 0x7C,
    0xBE, 0x97, 0xF1, 0xB1, 0xB1, 0x86, 0x3A, 0xEC,
    0x7B, 0x40, 0xD9, 0x01, 0x57, 0x62, 0x30, 0xBD,
    0x69, 0xEF, 0x8F, 0x6A, 0xEA, 0xFE, 0xB2, 0xB0,
    0x92, 0x19, 0xFA, 0x8F, 0xAF, 0x83, 0x37, 0x68,
    0x42, 0xB1, 0xB2, 0xAA, 0x9E, 0xF6, 0x8D, 0x79,
    0xDA, 0xAB, 0x89, 0xAF, 0x3F, 0xAB, 0xE4, 0x9A,
    0xCC, 0x27, 0x86, 0x38, 0x70, 0x73, 0x45, 0xBB,
    0xF1, 0x53, 0x44, 0xED, 0x79, 0xF7, 0xF4, 0x39,
    0x0E, 0xF8, 0xAC, 0x50, 0x9B, 0x56, 0xF3, 0x9A,
    0x98, 0x56, 0x65, 0x27, 0xA4, 0x1D, 0x3C, 0xBD,
    0x5E, 0x05, 0x58, 0xC1, 0x59, 0x92, 0x7D, 0xB0,
    0xE8, 0x84, 0x54, 0xA5, 0xD9, 0x64, 0x71, 0xFD,
    0xDC, 0xB5, 0x6D, 0x5B, 0xB0, 0x6B, 0xFA, 0x34,
    0x0E, 0xA7, 0xA1, 0x51, 0xEF, 0x1C, 0xA6, 0xFA,
    0x57, 0x2B, 0x76, 0xF3, 0xB1, 0xB9, 0x5D, 0x8C,
    0x85, 0x83, 0xD3, 0xE4, 0x77, 0x05, 0x36, 0xB8,
    0x4F, 0x01, 0x7E, 0x70, 0xE6, 0xFB, 0xF1, 0x76,
    0x60, 0x1A, 0x02, 0x66, 0x94, 0x1A, 0x17, 0xB0,
    0xC8, 0xB9, 0x7F, 0x4E, 0x74, 0xC2, 0xC1, 0xFF,
    0xC7, 0x27, 0x89, 0x19, 0x77, 0x79, 0x40, 0xC1,
    0xE1, 0xFF, 0x1D, 0x8D, 0xA6, 0x37, 0xD6, 0xB9,
    0x9D, 0xDA, 0xFE, 0x5E, 0x17, 0x61, 0x10, 0x02,
    0xE2, 0xC7, 0x78, 0xC1, 0xBE, 0x8B, 0x41, 0xD9,
    0x63, 0x79, 0xA5, 0x13, 0x60, 0xD9, 0x77, 0xFD,
    0x44, 0x35, 0xA1, 0x1C, 0x30, 0x8F, 0xE7, 0xEE,
    0x6F, 0x1A, 0xAD, 0x9D, 0xB2, 0x8C, 0x81, 0xAD,
    0xDE, 0x1A, 0x7A, 0x6F, 0x7C, 0xCE, 0x01, 0x1C,
    0x30, 0xDA, 0x37, 0xE4, 0xEB, 0x73, 0x64, 0x83,
    0xBD, 0x6C, 0x8E, 0x93, 0x48, 0xFB, 0xFB, 0xF7,
    0x2C, 0xC6, 0x58, 0x7D, 0x60, 0xC3, 0x6C, 0x8E,
    0x57, 0x7F, 0x09, 0x84, 0xC2, 0x89, 0xC9, 0x38,
    0x5A, 0x09, 0x86, 0x49, 0xDE, 0x21, 0xBC, 0xA2,
    0x7A, 0x7E, 0xA2, 0x29, 0x71, 0x6B, 0xA6, 0xE9,
    0xB2, 0x79, 0x71, 0x0F, 0x38, 0xFA, 0xA5, 0xFF,
    0xAE, 0x57, 0x41, 0x55, 0xCE, 0x4E, 0xFB, 0x4F,
    0x74, 0x36, 0x95, 0xE2, 0x91, 0x1B, 0x1D, 0x06,
    0xD5, 0xE2, 0x90, 0xCB, 0xCD, 0x86, 0xF5, 0x6D,
    0x0E, 0xDF, 0xCD, 0x21, 0x6A, 0xE2, 0x24, 0x27,
    0x05, 0x5E, 0x68, 0x35, 0xFD, 0x29, 0xEE, 0xF7,
    0x9E, 0x0D, 0x90, 0x77, 0x1F, 0xEA, 0xCE, 0xBE,
    0x12, 0xF2, 0x0E, 0x95, 0xB3, 0x4F, 0x0F, 0x78,
    0xB7, 0x37, 0xA9, 0x61, 0x8B, 0x26, 0xFA, 0x7D,
    0xBC, 0x98, 0x74, 0xF2, 0x72, 0xC4, 0x2B, 0xDB,
    0x56, 0x3E, 0xAF, 0xA1, 0x6B, 0x4F, 0xB6, 0x8C,
    0x3B, 0xB1, 0xE7, 0x8E, 0xAA, 0x81, 0xA0, 0x02,
    0x43, 0xFA, 0xAD, 0xD2, 0xBF, 0x18, 0xE6, 0x3D,
    0x38, 0x9A, 0xE4, 0x43, 0x77, 0xDA, 0x18, 0xC5,
    0x76, 0xB5, 0x0F, 0x00, 0x96, 0xCF, 0x34, 0x19,
    0x54, 0x83, 0xB0, 0x05, 0x48, 0xC0, 0x98, 0x62,
    0x36, 0xE3, 0xBC, 0x7C, 0xB8, 0xD6, 0x80, 0x1C,
    0x04, 0x94, 0xCC, 0xD1, 0x99, 0xE5, 0xC5, 0xBD,
    0x0D, 0x0E, 0xDC, 0x9E, 0xB8, 0xA0, 0x00, 0x1E,
    0x15, 0x27, 0x67, 0x54, 0xFC, 0xC6, 0x85, 0x66,
    0x05, 0x41, 0x48, 0xE6, 0xE7, 0x64, 0xBE, 0xE7,
    0xC7, 0x64, 0xDA, 0xAD, 0x3F, 0xC4, 0x52, 0x35,
    0xA6, 0xDA, 0xD4, 0x28, 0xFA, 0x20, 0xC1, 0x70,
    0xE3, 0x45, 0x00, 0x3F, 0x2F, 0x06, 0xEC, 0x81,
    0x05, 0xFE, 0xB2, 0x5B, 0x22, 0x81, 0xB6, 0x3D,
    0x27, 0x33, 0xBE, 0x96, 0x1C, 0x29, 0x95, 0x1D,
    0x11, 0xDD, 0x22, 0x21, 0x65, 0x7A, 0x9F, 0x53,
    0x1D, 0xDA, 0x2A, 0x19, 0x4D, 0xBB, 0x12, 0x64,
    0x48, 0xBD, 0xEE, 0xB2, 0x58, 0xE0, 0x7E, 0xA6,
    0x59, 0xC7, 0x46, 0x19, 0xA6, 0x38, 0x0E, 0x1D,
    0x66, 0xD6, 0x83, 0x2B, 0xFE, 0x67, 0xF6, 0x38,
    0xCD, 0x8F, 0xAE, 0x1F, 0x27, 0x23, 0x02, 0x0F,
    0x9C, 0x40, 0xA3, 0xFD, 0xA6, 0x7E, 0xDA, 0x3B,
    0xD2, 0x92, 0x38, 0xFB, 0xD4, 0xD4, 0xB4, 0x88,
    0x5C, 0x2A, 0x99, 0x17, 0x6D, 0xB1, 0xA0, 0x6C,
    0x50, 0x07, 0x78, 0x49, 0x1A, 0x82, 0x88, 0xF1,
    0x85, 0x5F, 0x60, 0xFF, 0xFC, 0xF1, 0xD1, 0x37,
    0x3F, 0xD9, 0x4F, 0xC6, 0x0C, 0x18, 0x11, 0xE1,
    0xAC, 0x3F, 0x1C, 0x6D, 0x00, 0x3B, 0xEC, 0xDA,
    0x3B, 0x1F, 0x27, 0x25, 0xCA, 0x59, 0x5D, 0xE0,
    0xCA, 0x63, 0x32, 0x8F, 0x3B, 0xE5, 0x7C, 0xC9,
    0x77, 0x55, 0x60, 0x11, 0x95, 0x14, 0x0D, 0xFB,
    0x59, 0xD3, 0x9C, 0xE0, 0x91, 0x30, 0x8B, 0x41,
    0x05, 0x74, 0x6D, 0xAC, 0x23, 0xD3, 0x3E, 0x5F,
    0x7C, 0xE4, 0x84, 0x8D, 0xA3, 0x16, 0xA9, 0xC6,
    0x6B, 0x95, 0x81, 0xBA, 0x35, 0x73, 0xBF, 0xAF,
    0x31, 0x14, 0x96, 0x18, 0x8A, 0xB1, 0x54, 0x23,
    0x28, 0x2E, 0xE4, 0x16, 0xDC, 0x2A, 0x19, 0xC5,
    0x72, 0x4F, 0xA9, 0x1A, 0xE4, 0xAD, 0xC8, 0x8B,
    0xC6, 0x67, 0x96, 0xEA, 0xE5, 0x67, 0x7A, 0x01,
    0xF6, 0x4E, 0x8C, 0x08, 0x63, 0x13, 0x95, 0x82,
    0x2D, 0x9D, 0xB8, 0xFC, 0xEE, 0x35, 0xC0, 0x6B,
    0x1F, 0xEE, 0xA5, 0x47, 0x4D, 0x6D, 0x8F, 0x34,
    0xB1, 0x53, 0x4A, 0x93, 0x6A, 0x18, 0xB0, 0xE0,
    0xD2, 0x0E, 0xAB, 0x86, 0xBC, 0x9C, 0x6D, 0x6A,
    0x52, 0x07, 0x19, 0x4E, 0x68, 0x72, 0x07, 0x32,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
#endif /* HAVE_FFDHE_Q */

#ifdef HAVE_PUBLIC_FFDHE
const DhParams* wc_Dh_ffdhe6144_Get(void)
{
    static const DhParams ffdhe6144 = {
        #ifdef HAVE_FFDHE_Q
            dh_ffdhe6144_q, sizeof(dh_ffdhe6144_q),
        #endif /* HAVE_FFDHE_Q */
        dh_ffdhe6144_p, sizeof(dh_ffdhe6144_p),
        dh_ffdhe6144_g, sizeof(dh_ffdhe6144_g)
    };
    return &ffdhe6144;
}
#endif
#endif

#ifdef HAVE_FFDHE_8192
static const byte dh_ffdhe8192_p[] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
    0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC,
    0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B,
    0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38,
    0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07,
    0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE,
    0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C,
    0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70,
    0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44,
    0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3,
    0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF,
    0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E,
    0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D,
    0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA,
    0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E,
    0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF,
    0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C,
    0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1,
    0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB,
    0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6,
    0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18,
    0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04,
    0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A,
    0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A,
    0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32,
    0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4,
    0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38,
    0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A,
    0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C,
    0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC,
    0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF,
    0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B,
    0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1,
    0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02,
    0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A,
    0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A,
    0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6,
    0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8,
    0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C,
    0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A,
    0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71,
    0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F,
    0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77,
    0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10,
    0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8,
    0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3,
    0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E,
    0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3,
    0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4,
    0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1,
    0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92,
    0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6,
    0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82,
    0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE,
    0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C,
    0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E,
    0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46,
    0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A,
    0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17,
    0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03,
    0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04,
    0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6,
    0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69,
    0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1,
    0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4,
    0xA4, 0x0E, 0x32, 0x9C, 0xCF, 0xF4, 0x6A, 0xAA,
    0x36, 0xAD, 0x00, 0x4C, 0xF6, 0x00, 0xC8, 0x38,
    0x1E, 0x42, 0x5A, 0x31, 0xD9, 0x51, 0xAE, 0x64,
    0xFD, 0xB2, 0x3F, 0xCE, 0xC9, 0x50, 0x9D, 0x43,
    0x68, 0x7F, 0xEB, 0x69, 0xED, 0xD1, 0xCC, 0x5E,
    0x0B, 0x8C, 0xC3, 0xBD, 0xF6, 0x4B, 0x10, 0xEF,
    0x86, 0xB6, 0x31, 0x42, 0xA3, 0xAB, 0x88, 0x29,
    0x55, 0x5B, 0x2F, 0x74, 0x7C, 0x93, 0x26, 0x65,
    0xCB, 0x2C, 0x0F, 0x1C, 0xC0, 0x1B, 0xD7, 0x02,
    0x29, 0x38, 0x88, 0x39, 0xD2, 0xAF, 0x05, 0xE4,
    0x54, 0x50, 0x4A, 0xC7, 0x8B, 0x75, 0x82, 0x82,
    0x28, 0x46, 0xC0, 0xBA, 0x35, 0xC3, 0x5F, 0x5C,
    0x59, 0x16, 0x0C, 0xC0, 0x46, 0xFD, 0x82, 0x51,
    0x54, 0x1F, 0xC6, 0x8C, 0x9C, 0x86, 0xB0, 0x22,
    0xBB, 0x70, 0x99, 0x87, 0x6A, 0x46, 0x0E, 0x74,
    0x51, 0xA8, 0xA9, 0x31, 0x09, 0x70, 0x3F, 0xEE,
    0x1C, 0x21, 0x7E, 0x6C, 0x38, 0x26, 0xE5, 0x2C,
    0x51, 0xAA, 0x69, 0x1E, 0x0E, 0x42, 0x3C, 0xFC,
    0x99, 0xE9, 0xE3, 0x16, 0x50, 0xC1, 0x21, 0x7B,
    0x62, 0x48, 0x16, 0xCD, 0xAD, 0x9A, 0x95, 0xF9,
    0xD5, 0xB8, 0x01, 0x94, 0x88, 0xD9, 0xC0, 0xA0,
    0xA1, 0xFE, 0x30, 0x75, 0xA5, 0x77, 0xE2, 0x31,
    0x83, 0xF8, 0x1D, 0x4A, 0x3F, 0x2F, 0xA4, 0x57,
    0x1E, 0xFC, 0x8C, 0xE0, 0xBA, 0x8A, 0x4F, 0xE8,
    0xB6, 0x85, 0x5D, 0xFE, 0x72, 0xB0, 0xA6, 0x6E,
    0xDE, 0xD2, 0xFB, 0xAB, 0xFB, 0xE5, 0x8A, 0x30,
    0xFA, 0xFA, 0xBE, 0x1C, 0x5D, 0x71, 0xA8, 0x7E,
    0x2F, 0x74, 0x1E, 0xF8, 0xC1, 0xFE, 0x86, 0xFE,
    0xA6, 0xBB, 0xFD, 0xE5, 0x30, 0x67, 0x7F, 0x0D,
    0x97, 0xD1, 0x1D, 0x49, 0xF7, 0xA8, 0x44, 0x3D,
    0x08, 0x22, 0xE5, 0x06, 0xA9, 0xF4, 0x61, 0x4E,
    0x01, 0x1E, 0x2A, 0x94, 0x83, 0x8F, 0xF8, 0x8C,
    0xD6, 0x8C, 0x8B, 0xB7, 0xC5, 0xC6, 0x42, 0x4C,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const byte dh_ffdhe8192_g[] = { 0x02 };
#ifdef HAVE_FFDHE_Q
static const byte dh_ffdhe8192_q[] = {
    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xD6, 0xFC, 0x2A, 0x2C, 0x51, 0x5D, 0xA5, 0x4D,
    0x57, 0xEE, 0x2B, 0x10, 0x13, 0x9E, 0x9E, 0x78,
    0xEC, 0x5C, 0xE2, 0xC1, 0xE7, 0x16, 0x9B, 0x4A,
    0xD4, 0xF0, 0x9B, 0x20, 0x8A, 0x32, 0x19, 0xFD,
    0xE6, 0x49, 0xCE, 0xE7, 0x12, 0x4D, 0x9F, 0x7C,
    0xBE, 0x97, 0xF1, 0xB1, 0xB1, 0x86, 0x3A, 0xEC,
    0x7B, 0x40, 0xD9, 0x01, 0x57, 0x62, 0x30, 0xBD,
    0x69, 0xEF, 0x8F, 0x6A, 0xEA, 0xFE, 0xB2, 0xB0,
    0x92, 0x19, 0xFA, 0x8F, 0xAF, 0x83, 0x37, 0x68,
    0x42, 0xB1, 0xB2, 0xAA, 0x9E, 0xF6, 0x8D, 0x79,
    0xDA, 0xAB, 0x89, 0xAF, 0x3F, 0xAB, 0xE4, 0x9A,
    0xCC, 0x27, 0x86, 0x38, 0x70, 0x73, 0x45, 0xBB,
    0xF1, 0x53, 0x44, 0xED, 0x79, 0xF7, 0xF4, 0x39,
    0x0E, 0xF8, 0xAC, 0x50, 0x9B, 0x56, 0xF3, 0x9A,
    0x98, 0x56, 0x65, 0x27, 0xA4, 0x1D, 0x3C, 0xBD,
    0x5E, 0x05, 0x58, 0xC1, 0x59, 0x92, 0x7D, 0xB0,
    0xE8, 0x84, 0x54, 0xA5, 0xD9, 0x64, 0x71, 0xFD,
    0xDC, 0xB5, 0x6D, 0x5B, 0xB0, 0x6B, 0xFA, 0x34,
    0x0E, 0xA7, 0xA1, 0x51, 0xEF, 0x1C, 0xA6, 0xFA,
    0x57, 0x2B, 0x76, 0xF3, 0xB1, 0xB9, 0x5D, 0x8C,
    0x85, 0x83, 0xD3, 0xE4, 0x77, 0x05, 0x36, 0xB8,
    0x4F, 0x01, 0x7E, 0x70, 0xE6, 0xFB, 0xF1, 0x76,
    0x60, 0x1A, 0x02, 0x66, 0x94, 0x1A, 0x17, 0xB0,
    0xC8, 0xB9, 0x7F, 0x4E, 0x74, 0xC2, 0xC1, 0xFF,
    0xC7, 0x27, 0x89, 0x19, 0x77, 0x79, 0x40, 0xC1,
    0xE1, 0xFF, 0x1D, 0x8D, 0xA6, 0x37, 0xD6, 0xB9,
    0x9D, 0xDA, 0xFE, 0x5E, 0x17, 0x61, 0x10, 0x02,
    0xE2, 0xC7, 0x78, 0xC1, 0xBE, 0x8B, 0x41, 0xD9,
    0x63, 0x79, 0xA5, 0x13, 0x60, 0xD9, 0x77, 0xFD,
    0x44, 0x35, 0xA1, 0x1C, 0x30, 0x8F, 0xE7, 0xEE,
    0x6F, 0x1A, 0xAD, 0x9D, 0xB2, 0x8C, 0x81, 0xAD,
    0xDE, 0x1A, 0x7A, 0x6F, 0x7C, 0xCE, 0x01, 0x1C,
    0x30, 0xDA, 0x37, 0xE4, 0xEB, 0x73, 0x64, 0x83,
    0xBD, 0x6C, 0x8E, 0x93, 0x48, 0xFB, 0xFB, 0xF7,
    0x2C, 0xC6, 0x58, 0x7D, 0x60, 0xC3, 0x6C, 0x8E,
    0x57, 0x7F, 0x09, 0x84, 0xC2, 0x89, 0xC9, 0x38,
    0x5A, 0x09, 0x86, 0x49, 0xDE, 0x21, 0xBC, 0xA2,
    0x7A, 0x7E, 0xA2, 0x29, 0x71, 0x6B, 0xA6, 0xE9,
    0xB2, 0x79, 0x71, 0x0F, 0x38, 0xFA, 0xA5, 0xFF,
    0xAE, 0x57, 0x41, 0x55, 0xCE, 0x4E, 0xFB, 0x4F,
    0x74, 0x36, 0x95, 0xE2, 0x91, 0x1B, 0x1D, 0x06,
    0xD5, 0xE2, 0x90, 0xCB, 0xCD, 0x86, 0xF5, 0x6D,
    0x0E, 0xDF, 0xCD, 0x21, 0x6A, 0xE2, 0x24, 0x27,
    0x05, 0x5E, 0x68, 0x35, 0xFD, 0x29, 0xEE, 0xF7,
    0x9E, 0x0D, 0x90, 0x77, 0x1F, 0xEA, 0xCE, 0xBE,
    0x12, 0xF2, 0x0E, 0x95, 0xB3, 0x4F, 0x0F, 0x78,
    0xB7, 0x37, 0xA9, 0x61, 0x8B, 0x26, 0xFA, 0x7D,
    0xBC, 0x98, 0x74, 0xF2, 0x72, 0xC4, 0x2B, 0xDB,
    0x56, 0x3E, 0xAF, 0xA1, 0x6B, 0x4F, 0xB6, 0x8C,
    0x3B, 0xB1, 0xE7, 0x8E, 0xAA, 0x81, 0xA0, 0x02,
    0x43, 0xFA, 0xAD, 0xD2, 0xBF, 0x18, 0xE6, 0x3D,
    0x38, 0x9A, 0xE4, 0x43, 0x77, 0xDA, 0x18, 0xC5,
    0x76, 0xB5, 0x0F, 0x00, 0x96, 0xCF, 0x34, 0x19,
    0x54, 0x83, 0xB0, 0x05, 0x48, 0xC0, 0x98, 0x62,
    0x36, 0xE3, 0xBC, 0x7C, 0xB8, 0xD6, 0x80, 0x1C,
    0x04, 0x94, 0xCC, 0xD1, 0x99, 0xE5, 0xC5, 0xBD,
    0x0D, 0x0E, 0xDC, 0x9E, 0xB8, 0xA0, 0x00, 0x1E,
    0x15, 0x27, 0x67, 0x54, 0xFC, 0xC6, 0x85, 0x66,
    0x05, 0x41, 0x48, 0xE6, 0xE7, 0x64, 0xBE, 0xE7,
    0xC7, 0x64, 0xDA, 0xAD, 0x3F, 0xC4, 0x52, 0x35,
    0xA6, 0xDA, 0xD4, 0x28, 0xFA, 0x20, 0xC1, 0x70,
    0xE3, 0x45, 0x00, 0x3F, 0x2F, 0x06, 0xEC, 0x81,
    0x05, 0xFE, 0xB2, 0x5B, 0x22, 0x81, 0xB6, 0x3D,
    0x27, 0x33, 0xBE, 0x96, 0x1C, 0x29, 0x95, 0x1D,
    0x11, 0xDD, 0x22, 0x21, 0x65, 0x7A, 0x9F, 0x53,
    0x1D, 0xDA, 0x2A, 0x19, 0x4D, 0xBB, 0x12, 0x64,
    0x48, 0xBD, 0xEE, 0xB2, 0x58, 0xE0, 0x7E, 0xA6,
    0x59, 0xC7, 0x46, 0x19, 0xA6, 0x38, 0x0E, 0x1D,
    0x66, 0xD6, 0x83, 0x2B, 0xFE, 0x67, 0xF6, 0x38,
    0xCD, 0x8F, 0xAE, 0x1F, 0x27, 0x23, 0x02, 0x0F,
    0x9C, 0x40, 0xA3, 0xFD, 0xA6, 0x7E, 0xDA, 0x3B,
    0xD2, 0x92, 0x38, 0xFB, 0xD4, 0xD4, 0xB4, 0x88,
    0x5C, 0x2A, 0x99, 0x17, 0x6D, 0xB1, 0xA0, 0x6C,
    0x50, 0x07, 0x78, 0x49, 0x1A, 0x82, 0x88, 0xF1,
    0x85, 0x5F, 0x60, 0xFF, 0xFC, 0xF1, 0xD1, 0x37,
    0x3F, 0xD9, 0x4F, 0xC6, 0x0C, 0x18, 0x11, 0xE1,
    0xAC, 0x3F, 0x1C, 0x6D, 0x00, 0x3B, 0xEC, 0xDA,
    0x3B, 0x1F, 0x27, 0x25, 0xCA, 0x59, 0x5D, 0xE0,
    0xCA, 0x63, 0x32, 0x8F, 0x3B, 0xE5, 0x7C, 0xC9,
    0x77, 0x55, 0x60, 0x11, 0x95, 0x14, 0x0D, 0xFB,
    0x59, 0xD3, 0x9C, 0xE0, 0x91, 0x30, 0x8B, 0x41,
    0x05, 0x74, 0x6D, 0xAC, 0x23, 0xD3, 0x3E, 0x5F,
    0x7C, 0xE4, 0x84, 0x8D, 0xA3, 0x16, 0xA9, 0xC6,
    0x6B, 0x95, 0x81, 0xBA, 0x35, 0x73, 0xBF, 0xAF,
    0x31, 0x14, 0x96, 0x18, 0x8A, 0xB1, 0x54, 0x23,
    0x28, 0x2E, 0xE4, 0x16, 0xDC, 0x2A, 0x19, 0xC5,
    0x72, 0x4F, 0xA9, 0x1A, 0xE4, 0xAD, 0xC8, 0x8B,
    0xC6, 0x67, 0x96, 0xEA, 0xE5, 0x67, 0x7A, 0x01,
    0xF6, 0x4E, 0x8C, 0x08, 0x63, 0x13, 0x95, 0x82,
    0x2D, 0x9D, 0xB8, 0xFC, 0xEE, 0x35, 0xC0, 0x6B,
    0x1F, 0xEE, 0xA5, 0x47, 0x4D, 0x6D, 0x8F, 0x34,
    0xB1, 0x53, 0x4A, 0x93, 0x6A, 0x18, 0xB0, 0xE0,
    0xD2, 0x0E, 0xAB, 0x86, 0xBC, 0x9C, 0x6D, 0x6A,
    0x52, 0x07, 0x19, 0x4E, 0x67, 0xFA, 0x35, 0x55,
    0x1B, 0x56, 0x80, 0x26, 0x7B, 0x00, 0x64, 0x1C,
    0x0F, 0x21, 0x2D, 0x18, 0xEC, 0xA8, 0xD7, 0x32,
    0x7E, 0xD9, 0x1F, 0xE7, 0x64, 0xA8, 0x4E, 0xA1,
    0xB4, 0x3F, 0xF5, 0xB4, 0xF6, 0xE8, 0xE6, 0x2F,
    0x05, 0xC6, 0x61, 0xDE, 0xFB, 0x25, 0x88, 0x77,
    0xC3, 0x5B, 0x18, 0xA1, 0x51, 0xD5, 0xC4, 0x14,
    0xAA, 0xAD, 0x97, 0xBA, 0x3E, 0x49, 0x93, 0x32,
    0xE5, 0x96, 0x07, 0x8E, 0x60, 0x0D, 0xEB, 0x81,
    0x14, 0x9C, 0x44, 0x1C, 0xE9, 0x57, 0x82, 0xF2,
    0x2A, 0x28, 0x25, 0x63, 0xC5, 0xBA, 0xC1, 0x41,
    0x14, 0x23, 0x60, 0x5D, 0x1A, 0xE1, 0xAF, 0xAE,
    0x2C, 0x8B, 0x06, 0x60, 0x23, 0x7E, 0xC1, 0x28,
    0xAA, 0x0F, 0xE3, 0x46, 0x4E, 0x43, 0x58, 0x11,
    0x5D, 0xB8, 0x4C, 0xC3, 0xB5, 0x23, 0x07, 0x3A,
    0x28, 0xD4, 0x54, 0x98, 0x84, 0xB8, 0x1F, 0xF7,
    0x0E, 0x10, 0xBF, 0x36, 0x1C, 0x13, 0x72, 0x96,
    0x28, 0xD5, 0x34, 0x8F, 0x07, 0x21, 0x1E, 0x7E,
    0x4C, 0xF4, 0xF1, 0x8B, 0x28, 0x60, 0x90, 0xBD,
    0xB1, 0x24, 0x0B, 0x66, 0xD6, 0xCD, 0x4A, 0xFC,
    0xEA, 0xDC, 0x00, 0xCA, 0x44, 0x6C, 0xE0, 0x50,
    0x50, 0xFF, 0x18, 0x3A, 0xD2, 0xBB, 0xF1, 0x18,
    0xC1, 0xFC, 0x0E, 0xA5, 0x1F, 0x97, 0xD2, 0x2B,
    0x8F, 0x7E, 0x46, 0x70, 0x5D, 0x45, 0x27, 0xF4,
    0x5B, 0x42, 0xAE, 0xFF, 0x39, 0x58, 0x53, 0x37,
    0x6F, 0x69, 0x7D, 0xD5, 0xFD, 0xF2, 0xC5, 0x18,
    0x7D, 0x7D, 0x5F, 0x0E, 0x2E, 0xB8, 0xD4, 0x3F,
    0x17, 0xBA, 0x0F, 0x7C, 0x60, 0xFF, 0x43, 0x7F,
    0x53, 0x5D, 0xFE, 0xF2, 0x98, 0x33, 0xBF, 0x86,
    0xCB, 0xE8, 0x8E, 0xA4, 0xFB, 0xD4, 0x22, 0x1E,
    0x84, 0x11, 0x72, 0x83, 0x54, 0xFA, 0x30, 0xA7,
    0x00, 0x8F, 0x15, 0x4A, 0x41, 0xC7, 0xFC, 0x46,
    0x6B, 0x46, 0x45, 0xDB, 0xE2, 0xE3, 0x21, 0x26,
    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
#endif /* HAVE_FFDHE_Q */

#ifdef HAVE_PUBLIC_FFDHE
const DhParams* wc_Dh_ffdhe8192_Get(void)
{
    static const DhParams ffdhe8192 = {
        #ifdef HAVE_FFDHE_Q
            dh_ffdhe8192_q, sizeof(dh_ffdhe8192_q),
        #endif /* HAVE_FFDHE_Q */
        dh_ffdhe8192_p, sizeof(dh_ffdhe8192_p),
        dh_ffdhe8192_g, sizeof(dh_ffdhe8192_g)
    };
    return &ffdhe8192;
}
#endif
#endif

int wc_InitDhKey_ex(DhKey* key, void* heap, int devId)
{
    int ret = 0;

    if (key == NULL)
        return BAD_FUNC_ARG;

    key->heap = heap; /* for XMALLOC/XFREE in future */
    key->trustedGroup = 0;

#ifdef WOLFSSL_DH_EXTRA
    if (mp_init_multi(&key->p, &key->g, &key->q, &key->pub, &key->priv, NULL) != MP_OKAY)
#else
    if (mp_init_multi(&key->p, &key->g, &key->q, NULL, NULL, NULL) != MP_OKAY)
#endif
        return MEMORY_E;

#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_DH)
    /* handle as async */
    ret = wolfAsync_DevCtxInit(&key->asyncDev, WOLFSSL_ASYNC_MARKER_DH,
        key->heap, devId);
#else
    (void)devId;
#endif

    key->trustedGroup = 0;

#ifdef WOLFSSL_KCAPI_DH
    key->handle = NULL;
#endif

#ifdef WC_DH_NONBLOCK
    key->nb = NULL;
#endif

    return ret;
}

int wc_InitDhKey(DhKey* key)
{
    return wc_InitDhKey_ex(key, NULL, INVALID_DEVID);
}

#ifdef WC_DH_NONBLOCK
int wc_DhSetNonBlock(DhKey* key, DhNb* nb)
{
    if (key == NULL)
        return BAD_FUNC_ARG;

    if (nb != NULL) {
        XMEMSET(nb, 0, sizeof(DhNb));
    }

    /* Pass NULL to disable non-blocking mode. */
    key->nb = nb;

    return 0;
}
#endif


int wc_FreeDhKey(DhKey* key)
{
    if (key) {
        mp_clear(&key->p);
        mp_clear(&key->g);
        mp_clear(&key->q);
    #ifdef WOLFSSL_DH_EXTRA
        mp_clear(&key->pub);
        mp_forcezero(&key->priv);
    #endif

    #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_DH)
        wolfAsync_DevCtxFree(&key->asyncDev, WOLFSSL_ASYNC_MARKER_DH);
    #endif
    #ifdef WOLFSSL_KCAPI_DH
        KcapiDh_Free(key);
    #endif
    }
    return 0;
}


static int _ffc_validate_public_key(DhKey* key, const byte* pub, word32 pubSz,
       const byte* prime, word32 primeSz, int partial);
#if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_DH_KEYGEN)
static int _ffc_pairwise_consistency_test(DhKey* key,
        const byte* pub, word32 pubSz, const byte* priv, word32 privSz);
#endif

#ifndef WOLFSSL_KCAPI_DH

#ifndef WC_NO_RNG
/* if defined to not use floating point values do not compile in */
#ifndef WOLFSSL_DH_CONST
    static word32 DiscreteLogWorkFactor(word32 n)
    {
        /* assuming discrete log takes about the same time as factoring */
        if (n < 5)
            return 0;
        else
            return (word32)((double)2.4 * XPOW((double)n, 1.0/3.0) *
                    XPOW(XLOG((double)n), 2.0/3.0) - 5);
    }
#endif /* WOLFSSL_DH_CONST*/


/* if not using fixed points use DiscreteLogWorkFactor function for unusual size
   otherwise round up on size needed */
#ifndef WOLFSSL_DH_CONST
    #define WOLFSSL_DH_ROUND(x) WC_DO_NOTHING
#else
    #define WOLFSSL_DH_ROUND(x)   \
        do {                      \
            if ((x) % 128) {      \
                (x) &= 0xffffff80;\
                (x) += 128;       \
            }                     \
        }                         \
        while (0)
#endif


#ifndef WOLFSSL_NO_DH186
/* validate that (L,N) match allowed sizes from SP 800-56A, Section 5.5.1.1.
 * modLen - represents L, the size of p in bits
 * divLen - represents N, the size of q in bits
 * return 0 on success, -1 on error */
static int CheckDhLN(word32 modLen, word32 divLen)
{
    int ret = -1;

    switch (modLen) {
        /* FA */
        case 1024:
            if (divLen == 160)
                ret = 0;
            break;
        /* FB, FC */
        case 2048:
            if (divLen == 224 || divLen == 256)
                ret = 0;
            break;
        /* Per SP 800-56Ar3 Table 2 */
        case 3072:
            if (divLen == 256)
                ret = 0;
            break;
        default:
            break;
    }

    return ret;
}


/* Create DH private key
 *
 * Based on NIST SP 800-56Ar3
 * "5.6.1.1.3 Key Pair Generation Using Extra Random Bits"
 *
 * dh     - pointer to initialized DhKey structure, needs to have dh->q
 * rng    - pointer to initialized WC_RNG structure
 * priv   - output location for generated private key
 * privSz - IN/OUT, size of priv buffer, size of generated private key
 *
 * return 0 on success, negative on error */
static int GeneratePrivateDh186(DhKey* key, WC_RNG* rng, byte* priv,
                                word32* privSz)
{
    word32 qSz, pSz, cSz;
    int err;
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    mp_int* tmpQ = NULL;
    mp_int* tmpX = NULL;
    byte* cBuf = NULL;
#else
    mp_int tmpQ[1], tmpX[1];
    byte cBuf[DH_MAX_SIZE + 64 / WOLFSSL_BIT_SIZE];
#endif

    /* Pointer parameters validated by the public entry wc_DhGenerateKeyPair. */

    if (mp_iszero(&key->q) == MP_YES) {
        WOLFSSL_MSG("DH q parameter needed for FIPS 186-4 key generation");
        return BAD_FUNC_ARG;
    }

    /* Bound *privSz so cSz (= *privSz + 8) cannot exceed the cBuf capacity.
     * Note: DH_MAX_SIZE is documented as a bit count, but the cBuf declaration
     * above uses it directly as a byte count (cBuf is DH_MAX_SIZE + 8 bytes).
     * This check matches that convention so *privSz (in bytes) is bounded by
     * the actual byte capacity of cBuf. The same bound is applied to the
     * WOLFSSL_SMALL_STACK path to avoid unbounded heap allocation. */
    if (*privSz > DH_MAX_SIZE) {
        WOLFSSL_MSG("DH private key size exceeds DH_MAX_SIZE");
        return BAD_FUNC_ARG;
    }

    qSz = (word32)mp_unsigned_bin_size(&key->q);
    pSz = (word32)mp_unsigned_bin_size(&key->p);

    /* verify (L,N) pair bit lengths */
    /* Trusted primes don't need to be checked. */
    if (!key->trustedGroup &&
            CheckDhLN(pSz * WOLFSSL_BIT_SIZE, qSz * WOLFSSL_BIT_SIZE) != 0) {
        WOLFSSL_MSG("DH param sizes do not match SP 800-56A requirements");
        return BAD_FUNC_ARG;
    }

    /* generate extra 64 bits so that bias from mod function is negligible */
    cSz = *privSz + (64 / WOLFSSL_BIT_SIZE);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    cBuf = (byte*)XMALLOC(cSz, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (cBuf == NULL) {
        return MEMORY_E;
    }
    tmpQ = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (tmpQ == NULL) {
        XFREE(cBuf, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        return MEMORY_E;
    }
    tmpX = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (tmpX == NULL) {
        XFREE(cBuf, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        XFREE(tmpQ, key->heap, DYNAMIC_TYPE_DH);
        return MEMORY_E;
    }
#endif


    if ((err = mp_init_multi(tmpX, tmpQ, NULL, NULL, NULL, NULL))
                   != MP_OKAY) {
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
        XFREE(cBuf, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        XFREE(tmpQ, key->heap, DYNAMIC_TYPE_DH);
        XFREE(tmpX, key->heap, DYNAMIC_TYPE_DH);
#endif
        return err;
    }

#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Add("GeneratePrivateDh186 cBuf", cBuf, cSz); /* cppcheck-suppress uninitvar */
    mp_memzero_add("GeneratePrivateDh186 tmpX", tmpX);
#endif
    do {
        /* generate N+64 bits (c) from RBG into tmpX, making sure positive.
         * Hash_DRBG uses SHA-256 which matches maximum
         * requested_security_strength of (L,N) */
        err = wc_RNG_GenerateBlock(rng, cBuf, cSz);
        if (err == MP_OKAY)
            err = mp_read_unsigned_bin(tmpX, cBuf, cSz);
        if (err != MP_OKAY) {
            mp_forcezero(tmpX);
            mp_clear(tmpQ);
            ForceZero(cBuf, cSz);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
            XFREE(cBuf, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            XFREE(tmpQ, key->heap, DYNAMIC_TYPE_DH);
            XFREE(tmpX, key->heap, DYNAMIC_TYPE_DH);
#endif
            return err;
        }
    } while (mp_cmp_d(tmpX, 1) != MP_GT);

    ForceZero(cBuf, cSz);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    XFREE(cBuf, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
#elif defined(WOLFSSL_CHECK_MEM_ZERO)
    wc_MemZero_Check(cBuf, cSz);
#endif

    /* tmpQ: M = min(2^N,q) - 1 */
    if (err == MP_OKAY)
        err = mp_2expt(tmpQ, (int)*privSz * 8);

    if (err == MP_OKAY) {
        if (mp_cmp(tmpQ, &key->q) == MP_GT) {
            err = mp_copy(&key->q, tmpQ);
        }
    }

    if (err == MP_OKAY)
        err = mp_sub_d(tmpQ, 1, tmpQ);

    /* x = c mod (M), tmpX holds c */
    if (err == MP_OKAY)
        err = mp_mod(tmpX, tmpQ, tmpX);

    /* x = c mod (M) + 1 */
    if (err == MP_OKAY)
        err = mp_add_d(tmpX, 1, tmpX);

    /* copy tmpX into priv */
    if (err == MP_OKAY) {
        pSz = (word32)mp_unsigned_bin_size(tmpX);
        if (pSz > *privSz) {
            WOLFSSL_MSG("DH private key output buffer too small");
            err = BAD_FUNC_ARG;
        } else {
            *privSz = pSz;
            err = mp_to_unsigned_bin(tmpX, priv);
        }
    }

    mp_forcezero(tmpX);
    mp_clear(tmpQ);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    XFREE(tmpQ, key->heap, DYNAMIC_TYPE_DH);
    XFREE(tmpX, key->heap, DYNAMIC_TYPE_DH);
#elif defined(WOLFSSL_CHECK_MEM_ZERO)
    mp_memzero_check(tmpX);
#endif

    return err;
}
#endif /* WOLFSSL_NO_DH186 */
#endif /* !WC_NO_RNG */

static int GeneratePrivateDh(DhKey* key, WC_RNG* rng, byte* priv,
                             word32* privSz)
{
#ifndef WC_NO_RNG
    int ret = 0;
    word32 sz = 0;

    if (mp_iseven(&key->p) == MP_YES) {
        ret = MP_VAL;
    }
    else
#ifndef WOLFSSL_NO_DH186
    if (mp_iszero(&key->q) == MP_NO) {

        /* q param available, use NIST SP 800-56Ar3, "5.6.1.1.3 Key Pair
         * Generation Using Extra Random Bits" */
        ret = GeneratePrivateDh186(key, rng, priv, privSz);

    }
    else
#endif
    {

        sz = (word32)mp_unsigned_bin_size(&key->p);

        /* Table of predetermined values from the operation
           2 * DiscreteLogWorkFactor(sz * WOLFSSL_BIT_SIZE) /
           WOLFSSL_BIT_SIZE + 1
           Sizes in table checked against RFC 3526
         */
        WOLFSSL_DH_ROUND(sz); /* if using fixed points only, then round up */
        switch (sz) {
            case 128:  sz = 21; break;
            case 256:  sz = 29; break;
            case 384:  sz = 34; break;
            case 512:  sz = 39; break;
            case 640:  sz = 42; break;
            case 768:  sz = 46; break;
            case 896:  sz = 49; break;
            case 1024: sz = 52; break;
            default:
            #ifndef WOLFSSL_DH_CONST
                /* if using floating points and size of p is not in table */
                sz = min(sz, 2 * DiscreteLogWorkFactor(sz * WOLFSSL_BIT_SIZE) /
                                           WOLFSSL_BIT_SIZE + 1);
                break;
            #else
                return BAD_FUNC_ARG;
            #endif
        }

        if (sz > *privSz)
            ret = WC_KEY_SIZE_E;

        if (ret == 0)
            ret = wc_RNG_GenerateBlock(rng, priv, sz);

        if (ret == 0) {
            priv[0] |= 0x0C;
            *privSz = sz;
        }
    }

    return ret;
#else
    (void)key;
    (void)rng;
    (void)priv;
    (void)privSz;
    return NOT_COMPILED_IN;
#endif /* WC_NO_RNG */
}


static int GeneratePublicDh(DhKey* key, byte* priv, word32 privSz,
    byte* pub, word32* pubSz)
{
    int ret = 0;
#ifndef WOLFSSL_SP_MATH
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    mp_int* x;
    mp_int* y;
#else
    mp_int x[1];
    mp_int y[1];
#endif
#endif

    if (*pubSz < (word32)mp_unsigned_bin_size(&key->p)) {
        return WC_KEY_SIZE_E;
    }

#ifdef WOLFSSL_HAVE_SP_DH
#ifndef WOLFSSL_SP_NO_2048
    if (mp_count_bits(&key->p) == 2048)
        return sp_DhExp_2048(&key->g, priv, privSz, &key->p, pub, pubSz);
#endif
#ifndef WOLFSSL_SP_NO_3072
    if (mp_count_bits(&key->p) == 3072)
        return sp_DhExp_3072(&key->g, priv, privSz, &key->p, pub, pubSz);
#endif
#ifdef WOLFSSL_SP_4096
    if (mp_count_bits(&key->p) == 4096)
        return sp_DhExp_4096(&key->g, priv, privSz, &key->p, pub, pubSz);
#endif
#endif

#if !defined(WOLFSSL_SP_MATH)
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    x = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (x == NULL)
        return MEMORY_E;
    y = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (y == NULL) {
        XFREE(x, key->heap, DYNAMIC_TYPE_DH);
        return MEMORY_E;
    }
#endif
    if (mp_init_multi(x, y, 0, 0, 0, 0) != MP_OKAY) {
    #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
        XFREE(y, key->heap, DYNAMIC_TYPE_DH);
        XFREE(x, key->heap, DYNAMIC_TYPE_DH);
    #endif
        return MP_INIT_E;
    }

    if (mp_read_unsigned_bin(x, priv, privSz) != MP_OKAY)
        ret = MP_READ_E;

    if (ret == 0 && mp_exptmod(&key->g, x, &key->p, y) != MP_OKAY)
        ret = MP_EXPTMOD_E;

    if (ret == 0 && mp_to_unsigned_bin(y, pub) != MP_OKAY)
        ret = MP_TO_E;

    if (ret == 0)
        *pubSz = (word32)mp_unsigned_bin_size(y);

    mp_clear(y);
    mp_forcezero(x);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    XFREE(y, key->heap, DYNAMIC_TYPE_DH);
    XFREE(x, key->heap, DYNAMIC_TYPE_DH);
#endif
#else
    ret = WC_KEY_SIZE_E;
#endif

    return ret;
}

/**
 * Given a DhKey with set params and a priv key, generate the corresponding
 * public key. If fips, does pub key validation.
 * */
int wc_DhGeneratePublic(DhKey* key, byte* priv, word32 privSz,
    byte* pub, word32* pubSz)
{
    int ret = 0;

    if (key == NULL || priv == NULL || privSz == 0 ||
        pub == NULL || pubSz == NULL) {
        return BAD_FUNC_ARG;
    }

    SAVE_VECTOR_REGISTERS(return _svr_ret;);

    ret = GeneratePublicDh(key, priv, privSz, pub, pubSz);

    #if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_DH_KEYGEN)
    if (ret == 0)
        ret = _ffc_validate_public_key(key, pub, *pubSz, NULL, 0, 0);
    if (ret == 0)
        ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv, privSz);
    #endif /* FIPS V5 or later || WOLFSSL_VALIDATE_DH_KEYGEN */

    RESTORE_VECTOR_REGISTERS();

    return ret;
}

static int wc_DhGenerateKeyPair_Sync(DhKey* key, WC_RNG* rng,
    byte* priv, word32* privSz, byte* pub, word32* pubSz)
{
    int ret;

    if (key == NULL || rng == NULL || priv == NULL || privSz == NULL ||
        pub == NULL || pubSz == NULL) {
        return BAD_FUNC_ARG;
    }

    SAVE_VECTOR_REGISTERS(return _svr_ret;);

    ret = GeneratePrivateDh(key, rng, priv, privSz);

    if (ret == 0)
        ret = GeneratePublicDh(key, priv, *privSz, pub, pubSz);
#if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_DH_KEYGEN)
    if (ret == 0)
        ret = _ffc_validate_public_key(key, pub, *pubSz, NULL, 0, 0);
    if (ret == 0)
        ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv, *privSz);
#endif /* FIPS V5 or later || WOLFSSL_VALIDATE_DH_KEYGEN */


    RESTORE_VECTOR_REGISTERS();

    return ret;
}
#endif /* !WOLFSSL_KCAPI_DH */

#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_DH)
static int wc_DhGenerateKeyPair_Async(DhKey* key, WC_RNG* rng,
    byte* priv, word32* privSz, byte* pub, word32* pubSz)
{
    int ret;

#if defined(HAVE_INTEL_QA)
    word32 pBits;

    /* QAT DH sizes: 768, 1024, 1536, 2048, 3072 and 4096 bits */
    pBits = mp_unsigned_bin_size(&key->p) * 8;
    if (pBits == 768 ||  pBits == 1024 || pBits == 1536 ||
        pBits == 2048 || pBits == 3072 || pBits == 4096) {
        mp_int x;

        ret = mp_init(&x);
        if (ret != MP_OKAY)
            return ret;

        ret = GeneratePrivateDh(key, rng, priv, privSz);
        if (ret == 0)
            ret = mp_read_unsigned_bin(&x, priv, *privSz);
        if (ret == MP_OKAY)
            ret = wc_mp_to_bigint(&x, &x.raw);
        if (ret == MP_OKAY)
            ret = wc_mp_to_bigint(&key->p, &key->p.raw);
        if (ret == MP_OKAY)
            ret = wc_mp_to_bigint(&key->g, &key->g.raw);
        if (ret == MP_OKAY)
            ret = IntelQaDhKeyGen(&key->asyncDev, &key->p.raw, &key->g.raw,
                &x.raw, pub, pubSz);
        mp_forcezero(&x);

        return ret;
    }

#elif defined(HAVE_CAVIUM)
    /* TODO: Not implemented - use software for now */

#elif defined(WOLFSSL_ASYNC_CRYPT_SW)
    if (wc_AsyncSwInit(&key->asyncDev, ASYNC_SW_DH_GEN)) {
        WC_ASYNC_SW* sw = &key->asyncDev.sw;
        sw->dhGen.key = key;
        sw->dhGen.rng = rng;
        sw->dhGen.priv = priv;
        sw->dhGen.privSz = privSz;
        sw->dhGen.pub = pub;
        sw->dhGen.pubSz = pubSz;
        return WC_PENDING_E;
    }
#endif

    /* otherwise use software DH */
    ret = wc_DhGenerateKeyPair_Sync(key, rng, priv, privSz, pub, pubSz);

    return ret;
}
#endif /* WOLFSSL_ASYNC_CRYPT && WC_ASYNC_ENABLE_DH */


/* Check DH Public Key for invalid numbers, optionally allowing
 * the public key to be checked against the large prime (q).
 * If q is NULL, the q value of key is used.
 * Check per process in SP 800-56Ar3, section 5.6.2.3.1 or 2.
 *
 * key     DH key group parameters.
 * pub     Public Key.
 * pubSz   Public Key size.
 * prime   Large prime (q), optionally NULL to skip check
 * primeSz Size of large prime
 * partial Do the partial test process. (section 5.6.2.3.2)
 *
 *  returns 0 on success or error code
 */
static int _ffc_validate_public_key(DhKey* key, const byte* pub, word32 pubSz,
       const byte* prime, word32 primeSz, int partial)
{
    int ret = 0;
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    mp_int* y = NULL;
    mp_int* p = NULL;
    mp_int* q = NULL;
#else
    mp_int y[1];
    mp_int p[1];
    mp_int q[1];
#endif

    if (key == NULL || pub == NULL) {
        return BAD_FUNC_ARG;
    }

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    y = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (y == NULL)
        return MEMORY_E;
    p = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (p == NULL) {
        XFREE(y, key->heap, DYNAMIC_TYPE_DH);
        return MEMORY_E;
    }
    q = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (q == NULL) {
        XFREE(p, key->heap, DYNAMIC_TYPE_DH);
        XFREE(y, key->heap, DYNAMIC_TYPE_DH);
        return MEMORY_E;
    }
#endif

    if (mp_init_multi(y, p, q, NULL, NULL, NULL) != MP_OKAY) {
    #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
        XFREE(q, key->heap, DYNAMIC_TYPE_DH);
        XFREE(p, key->heap, DYNAMIC_TYPE_DH);
        XFREE(y, key->heap, DYNAMIC_TYPE_DH);
    #endif
        return MP_INIT_E;
    }

    SAVE_VECTOR_REGISTERS(ret = _svr_ret;);

    if (mp_read_unsigned_bin(y, pub, pubSz) != MP_OKAY) {
        ret = MP_READ_E;
    }

    if (ret == 0 && prime != NULL) {
        if (mp_read_unsigned_bin(q, prime, primeSz) != MP_OKAY)
            ret = MP_READ_E;

    } else if (mp_iszero(&key->q) == MP_NO) {
        /* use q available in DhKey */
        if (mp_copy(&key->q, q) != MP_OKAY)
            ret = MP_INIT_E;
    }

    /* SP 800-56Ar3, section 5.6.2.3.2 */
    /* pub (y) should not be 0 or 1 */
    if (ret == 0 && mp_cmp_d(y, 2) == MP_LT) {
        ret = MP_CMP_E;
    }

    /* pub (y) shouldn't be greater than or equal to p - 1 */
    if (ret == 0 && mp_copy(&key->p, p) != MP_OKAY) {
        ret = MP_INIT_E;
    }
    if (ret == 0 && mp_sub_d(p, 2, p) != MP_OKAY) {
        ret = MP_SUB_E;
    }
    if (ret == 0 && mp_cmp(y, p) == MP_GT) {
        ret = MP_CMP_E;
    }

    if (!partial) {
        if (ret == 0 && (prime != NULL || (mp_iszero(&key->q) == MP_NO) )) {

            /* restore key->p into p */
            if (mp_copy(&key->p, p) != MP_OKAY)
                ret = MP_INIT_E;
        }

        /* SP 800-56Ar3, section 5.6.2.3.1, process step 2 */
        if (ret == 0 && prime != NULL) {
#ifdef WOLFSSL_HAVE_SP_DH
#ifndef WOLFSSL_SP_NO_2048
            if (mp_count_bits(&key->p) == 2048) {
                ret = sp_ModExp_2048(y, q, p, y);
                if (ret != 0)
                    ret = MP_EXPTMOD_E;
            }
            else
#endif
#ifndef WOLFSSL_SP_NO_3072
            if (mp_count_bits(&key->p) == 3072) {
                ret = sp_ModExp_3072(y, q, p, y);
                if (ret != 0)
                    ret = MP_EXPTMOD_E;
            }
            else
#endif
#ifdef WOLFSSL_SP_4096
            if (mp_count_bits(&key->p) == 4096) {
                ret = sp_ModExp_4096(y, q, p, y);
                if (ret != 0)
                    ret = MP_EXPTMOD_E;
            }
            else
#endif
#endif

            {
#if !defined(WOLFSSL_SP_MATH)
                /* calculate (y^q) mod(p), store back into y */
                if (mp_exptmod(y, q, p, y) != MP_OKAY)
                    ret = MP_EXPTMOD_E;
#else
                ret = WC_KEY_SIZE_E;
#endif
            }

            /* verify above == 1 */
            if (ret == 0 && mp_cmp_d(y, 1) != MP_EQ)
                ret = MP_CMP_E;
        }
    }

    mp_clear(y);
    mp_clear(p);
    mp_clear(q);

    RESTORE_VECTOR_REGISTERS();

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    XFREE(q, key->heap, DYNAMIC_TYPE_DH);
    XFREE(p, key->heap, DYNAMIC_TYPE_DH);
    XFREE(y, key->heap, DYNAMIC_TYPE_DH);
#endif

    return ret;
}


/* Performs a full public-key validation routine. */
int wc_DhCheckPubKey_ex(DhKey* key, const byte* pub, word32 pubSz,
                        const byte* prime, word32 primeSz)
{
    return _ffc_validate_public_key(key, pub, pubSz, prime, primeSz, 0);
}


/* Check DH Public Key for invalid numbers. Performs a partial public-key
 * validation routine.
 *
 * key   DH key group parameters.
 * pub   Public Key.
 * pubSz Public Key size.
 *
 *  returns 0 on success or error code
 */
int wc_DhCheckPubKey(DhKey* key, const byte* pub, word32 pubSz)
{
    return _ffc_validate_public_key(key, pub, pubSz, NULL, 0, 1);
}


/**
 * Quick validity check of public key value against prime.
 * Checks are:
 *   - Public key not 0 or 1
 *   - Public key not equal to prime or prime - 1
 *   - Public key not bigger than prime.
 *
 * prime    Big-endian encoding of prime in bytes.
 * primeSz  Size of prime in bytes.
 * pub      Big-endian encoding of public key in bytes.
 * pubSz    Size of public key in bytes.
 */
int wc_DhCheckPubValue(const byte* prime, word32 primeSz, const byte* pub,
                       word32 pubSz)
{
    int ret = 0;
    word32 i;

    for (i = 0; i < pubSz && pub[i] == 0; i++) {
    }
    pubSz -= i;
    pub += i;

    if (pubSz == 0 || (pubSz == 1 && pub[0] == 1))
        ret = MP_VAL;
    else if (pubSz == primeSz) {
        for (i = 0; i < pubSz-1 && pub[i] == prime[i]; i++) {
        }
        if (i == pubSz-1 && (pub[i] == prime[i] || pub[i] == prime[i] - 1))
            ret = MP_VAL;
        else if (pub[i] > prime[i])
            ret = MP_VAL;
    }
    else if (pubSz > primeSz)
        ret = MP_VAL;

    return ret;
}


/* Check DH Private Key for invalid numbers, optionally allowing
 * the private key to be checked against the large prime (q).
 * Check per process in SP 800-56Ar3, section 5.6.2.1.2.
 *
 * key     DH key group parameters.
 * priv    Private Key.
 * privSz  Private Key size.
 * prime   Large prime (q), optionally NULL to skip check
 * primeSz Size of large prime
 *
 *  returns 0 on success or error code
 */
int wc_DhCheckPrivKey_ex(DhKey* key, const byte* priv, word32 privSz,
                         const byte* prime, word32 primeSz)
{
    int ret = 0;
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    mp_int* x = NULL;
    mp_int* q = NULL;
#else
    mp_int x[1];
    mp_int q[1];
#endif

    if (key == NULL || priv == NULL) {
        return BAD_FUNC_ARG;
    }

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    x = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (x == NULL)
        return MEMORY_E;
    q = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (q == NULL) {
        XFREE(x, key->heap, DYNAMIC_TYPE_DH);
        return MEMORY_E;
    }
#endif

    if (mp_init_multi(x, q, NULL, NULL, NULL, NULL) != MP_OKAY) {
    #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
        XFREE(q, key->heap, DYNAMIC_TYPE_DH);
        XFREE(x, key->heap, DYNAMIC_TYPE_DH);
    #endif
        return MP_INIT_E;
    }

    if (mp_read_unsigned_bin(x, priv, privSz) != MP_OKAY) {
        ret = MP_READ_E;
    }

    if (ret == 0) {
    #ifdef WOLFSSL_CHECK_MEM_ZERO
        mp_memzero_add("wc_DhCheckPrivKey_ex x", x);
    #endif
        if (prime != NULL) {
            if (mp_read_unsigned_bin(q, prime, primeSz) != MP_OKAY)
                ret = MP_READ_E;
        }
        else if (mp_iszero(&key->q) == MP_NO) {
            /* use q available in DhKey */
            if (mp_copy(&key->q, q) != MP_OKAY)
                ret = MP_INIT_E;
        }
    }

    /* priv (x) should not be 0 */
    if (ret == 0) {
        if (mp_cmp_d(x, 0) == MP_EQ)
            ret = MP_CMP_E;
    }

    if (ret == 0) {
        if (mp_iszero(q) == MP_NO) {
            /* priv (x) shouldn't be greater than q - 1 */
            if (mp_copy(&key->q, q) != MP_OKAY)
                ret = MP_INIT_E;
            if (ret == 0) {
                if (mp_sub_d(q, 1, q) != MP_OKAY)
                    ret = MP_SUB_E;
            }
            if (ret == 0) {
                if (mp_cmp(x, q) == MP_GT)
                    ret = DH_CHECK_PRIV_E;
            }
        }
    }

    mp_forcezero(x);
    mp_clear(q);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    XFREE(q, key->heap, DYNAMIC_TYPE_DH);
    XFREE(x, key->heap, DYNAMIC_TYPE_DH);
#elif defined(WOLFSSL_CHECK_MEM_ZERO)
    mp_memzero_check(x);
#endif

    return ret;
}


/* Check DH Private Key for invalid numbers
 *
 * key    DH key group parameters.
 * priv   Private Key.
 * privSz Private Key size.
 *
 *  returns 0 on success or error code
 */
int wc_DhCheckPrivKey(DhKey* key, const byte* priv, word32 privSz)
{
    return wc_DhCheckPrivKey_ex(key, priv, privSz, NULL, 0);
}


/* Performs a Pairwise Consistency Test on an FFC key pair. */
/* Check DH Keys for pair-wise consistency per process in
 * SP 800-56Ar3, section 5.6.2.1.4, method (b) for FFC. */
static int _ffc_pairwise_consistency_test(DhKey* key,
        const byte* pub, word32 pubSz, const byte* priv, word32 privSz)
{
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    mp_int* publicKey = NULL;
    mp_int* privateKey = NULL;
    mp_int* checkKey = NULL;
#else
    mp_int publicKey[1];
    mp_int privateKey[1];
    mp_int checkKey[1];
#endif
    int ret = 0;

    if (key == NULL || pub == NULL || priv == NULL)
        return BAD_FUNC_ARG;
    if (mp_iseven(&key->p) == MP_YES)
        return MP_VAL;

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    publicKey = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (publicKey == NULL)
        return MEMORY_E;
    privateKey = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (privateKey == NULL) {
        XFREE(publicKey, key->heap, DYNAMIC_TYPE_DH);
        return MEMORY_E;
    }
    checkKey = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (checkKey == NULL) {
        XFREE(privateKey, key->heap, DYNAMIC_TYPE_DH);
        XFREE(publicKey, key->heap, DYNAMIC_TYPE_DH);
        return MEMORY_E;
    }
#endif

    if (mp_init_multi(publicKey, privateKey, checkKey,
                      NULL, NULL, NULL) != MP_OKAY) {

    #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
        XFREE(privateKey, key->heap, DYNAMIC_TYPE_DH);
        XFREE(publicKey, key->heap, DYNAMIC_TYPE_DH);
        XFREE(checkKey, key->heap, DYNAMIC_TYPE_DH);
    #endif
        return MP_INIT_E;
    }

    SAVE_VECTOR_REGISTERS(ret = _svr_ret;);

    /* Load the private and public keys into big integers. */
    if (mp_read_unsigned_bin(publicKey, pub, pubSz) != MP_OKAY ||
        mp_read_unsigned_bin(privateKey, priv, privSz) != MP_OKAY) {

        ret = MP_READ_E;
    }
#ifdef WOLFSSL_CHECK_MEM_ZERO
    mp_memzero_add("_ffc_pairwise_consistency_test privateKey", privateKey);
#endif

    /* Calculate checkKey = g^privateKey mod p */
    if (ret == 0) {
#ifdef WOLFSSL_HAVE_SP_DH
#ifndef WOLFSSL_SP_NO_2048
        if (mp_count_bits(&key->p) == 2048) {
            ret = sp_ModExp_2048(&key->g, privateKey, &key->p, checkKey);
            if (ret != 0)
                ret = MP_EXPTMOD_E;
        }
        else
#endif
#ifndef WOLFSSL_SP_NO_3072
        if (mp_count_bits(&key->p) == 3072) {
            ret = sp_ModExp_3072(&key->g, privateKey, &key->p, checkKey);
            if (ret != 0)
                ret = MP_EXPTMOD_E;
        }
        else
#endif
#ifdef WOLFSSL_SP_4096
        if (mp_count_bits(&key->p) == 4096) {
            ret = sp_ModExp_4096(&key->g, privateKey, &key->p, checkKey);
            if (ret != 0)
                ret = MP_EXPTMOD_E;
        }
        else
#endif
#endif
        {
#if !defined(WOLFSSL_SP_MATH)
            if (mp_exptmod(&key->g, privateKey, &key->p, checkKey) != MP_OKAY)
                ret = MP_EXPTMOD_E;
#else
            ret = WC_KEY_SIZE_E;
#endif
        }
    }

    /* Compare the calculated public key to the supplied check value. */
    if (ret == 0) {
        if (mp_cmp(checkKey, publicKey) != MP_EQ)
            ret = MP_CMP_E;
    }

    mp_forcezero(privateKey);
    mp_clear(publicKey);
    mp_clear(checkKey);

    RESTORE_VECTOR_REGISTERS();

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    XFREE(checkKey, key->heap, DYNAMIC_TYPE_DH);
    XFREE(privateKey, key->heap, DYNAMIC_TYPE_DH);
    XFREE(publicKey, key->heap, DYNAMIC_TYPE_DH);
#elif defined(WOLFSSL_CHECK_MEM_ZERO)
    mp_memzero_check(privateKey);
#endif

    return ret;
}


/* Check DH Keys for pair-wise consistency per process in
 * SP 800-56Ar3, section 5.6.2.1.4, method (b) for FFC.
 *
 * key    DH key group parameters.
 * pub    Public Key.
 * pubSz  Public Key size.
 * priv   Private Key.
 * privSz Private Key size.
 *
 *  returns 0 on success or error code
 */
int wc_DhCheckKeyPair(DhKey* key, const byte* pub, word32 pubSz,
                      const byte* priv, word32 privSz)
{
    return _ffc_pairwise_consistency_test(key, pub, pubSz, priv, privSz);
}


int wc_DhGenerateKeyPair(DhKey* key, WC_RNG* rng,
    byte* priv, word32* privSz, byte* pub, word32* pubSz)
{
    int ret;

    if (key == NULL || rng == NULL || priv == NULL || privSz == NULL ||
                                                pub == NULL || pubSz == NULL) {
        return BAD_FUNC_ARG;
    }

#ifdef WOLFSSL_KCAPI_DH
    (void)priv;
    (void)privSz;
    ret = KcapiDh_MakeKey(key, pub, pubSz);
#else
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_DH)
    if (key->asyncDev.marker == WOLFSSL_ASYNC_MARKER_DH) {
        ret = wc_DhGenerateKeyPair_Async(key, rng, priv, privSz, pub, pubSz);
    }
    else
#endif
    {
        ret = wc_DhGenerateKeyPair_Sync(key, rng, priv, privSz, pub, pubSz);
    }
#endif /* WOLFSSL_KCAPI_DH */

    return ret;
}

#ifndef WOLFSSL_KCAPI_DH
static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz,
    const byte* priv, word32 privSz, const byte* otherPub, word32 pubSz, int ct)
{
    int ret = 0;
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    mp_int* y = NULL;
#if !defined(WOLFSSL_SP_MATH)
    mp_int* x = NULL;
    mp_int* z = NULL;
#endif
#else
    mp_int y[1];
#if !defined(WOLFSSL_SP_MATH)
    mp_int x[1];
    mp_int z[1];
#endif
#endif

    if (mp_iseven(&key->p) == MP_YES) {
        return MP_VAL;
    }

    /* Non-blocking re-entry: the same wc_DhAgree call repeats until the
     * SP state machine completes, so cache the per-op key validation
     * results instead of re-running them each yield. The cache is
     * scoped to non-blocking, non-const-time callers only. */
#ifdef WC_DH_NONBLOCK
    if (key->nb == NULL || ct || !key->nb->pubKeyValidated)
#endif
    {
#ifdef WOLFSSL_VALIDATE_FFC_IMPORT
        if (wc_DhCheckPrivKey(key, priv, privSz) != 0) {
            WOLFSSL_MSG("wc_DhAgree wc_DhCheckPrivKey failed");
            return DH_CHECK_PRIV_E;
        }
#endif
        /* Always validate peer public key (2 <= y <= p-2) per SP 800-56A */
        if (wc_DhCheckPubKey(key, otherPub, pubSz) != 0) {
            WOLFSSL_MSG("wc_DhAgree wc_DhCheckPubKey failed");
            return DH_CHECK_PUB_E;
        }
#ifdef WC_DH_NONBLOCK
        if (key->nb != NULL && !ct) {
            key->nb->pubKeyValidated = 1;
        }
#endif
    }

#if defined(WC_DH_NONBLOCK) && defined(WOLFSSL_HAVE_SP_DH) && \
    defined(WOLFSSL_SP_NONBLOCK) && defined(WOLFSSL_SP_SMALL) && \
    !defined(WOLFSSL_SP_FAST_MODEXP)
    /* Non-blocking dispatch bypasses the mp_int dance entirely - the SP
     * wrapper takes byte buffers and persists across yields. The constant-
     * time fold-back (ct branch) is intentionally not applied here; nb
     * callers should use the standard wc_DhAgree(). */
    if (key->nb != NULL && !ct) {
        int nb_ret = MP_OKAY;
        int dispatched = 0;
    #ifndef WOLFSSL_SP_NO_2048
        if (mp_count_bits(&key->p) == 2048) {
            nb_ret = sp_DhExp_2048_nb(&key->nb->sp_ctx, otherPub, pubSz,
                         priv, privSz, &key->p, agree, agreeSz);
            dispatched = 1;
        }
    #endif
    #ifndef WOLFSSL_SP_NO_3072
        if (!dispatched && mp_count_bits(&key->p) == 3072) {
            nb_ret = sp_DhExp_3072_nb(&key->nb->sp_ctx, otherPub, pubSz,
                         priv, privSz, &key->p, agree, agreeSz);
            dispatched = 1;
        }
    #endif
    #ifdef WOLFSSL_SP_4096
        if (!dispatched && mp_count_bits(&key->p) == 4096) {
            nb_ret = sp_DhExp_4096_nb(&key->nb->sp_ctx, otherPub, pubSz,
                         priv, privSz, &key->p, agree, agreeSz);
            dispatched = 1;
        }
    #endif
        if (dispatched) {
            /* Op finished (or hit a hard error) - clear the cached
             * validation so the next op on this DhNb re-runs the
             * SP 800-56A peer-key check. MP_WOULDBLOCK keeps it. */
            if (nb_ret != WC_NO_ERR_TRACE(MP_WOULDBLOCK)) {
                key->nb->pubKeyValidated = 0;
            }
            return nb_ret;
        }
        /* size not nb-supported - the blocking path below completes in
         * one call, so the cached validation is single-use. Clear it
         * here so the next agree on this DhNb re-validates. */
        key->nb->pubKeyValidated = 0;
        /* fall through to blocking path */
    }
#endif

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    y = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (y == NULL)
        return MEMORY_E;
#if !defined(WOLFSSL_SP_MATH)
    x = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (x == NULL) {
        XFREE(y, key->heap, DYNAMIC_TYPE_DH);
        return MEMORY_E;
    }
    z = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (z == NULL) {
        XFREE(x, key->heap, DYNAMIC_TYPE_DH);
        XFREE(y, key->heap, DYNAMIC_TYPE_DH);
        return MEMORY_E;
    }
#endif
#endif

#ifdef WOLFSSL_HAVE_SP_DH
    if (0
#ifndef WOLFSSL_SP_NO_2048
        || mp_count_bits(&key->p) == 2048
#endif
#ifndef WOLFSSL_SP_NO_3072
        || mp_count_bits(&key->p) == 3072
#endif
#ifdef WOLFSSL_SP_4096
        || mp_count_bits(&key->p) == 4096
#endif
       ) {
        int i = (int)*agreeSz - 1;

        if (mp_init(y) != MP_OKAY)
            ret = MP_INIT_E;

        if (ret == 0) {
            SAVE_VECTOR_REGISTERS(ret = _svr_ret;);

            if (ret == 0 && mp_read_unsigned_bin(y, otherPub, pubSz) != MP_OKAY)
                ret = MP_READ_E;

            if (ret == 0) {
            #ifndef WOLFSSL_SP_NO_2048
                if (mp_count_bits(&key->p) == 2048) {
                    ret = sp_DhExp_2048(y, priv, privSz, &key->p, agree,
                                        agreeSz);
                }
            #endif
            #ifndef WOLFSSL_SP_NO_3072
                if (mp_count_bits(&key->p) == 3072) {
                    ret = sp_DhExp_3072(y, priv, privSz, &key->p, agree,
                                        agreeSz);
                }
            #endif
            #ifdef WOLFSSL_SP_4096
                if (mp_count_bits(&key->p) == 4096) {
                    ret = sp_DhExp_4096(y, priv, privSz, &key->p, agree,
                                        agreeSz);
                }
            #endif
            }

            mp_clear(y);

            RESTORE_VECTOR_REGISTERS();
        }

        /* make sure agree is > 1 (SP800-56A, 5.7.1.1) */
        if ((ret == 0) &&
            ((*agreeSz == 0) || ((*agreeSz == 1) && (agree[0] == 1))))
        {
            ret = MP_VAL;
        }

        if ((ret == 0) && ct) {
            volatile word16 mask = 0xff;
            sword16 o = (sword16)(*agreeSz - 1);

            *agreeSz = (word32)(i + 1);
            for (; i >= 0 ; i--) {
                agree[i] = agree[o] & (byte)mask;
                mask = ctMask16LT(0, (int)o);
                o = (sword16)(o + (sword16)mask);
            }
        }

    #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    #if !defined(WOLFSSL_SP_MATH)
        XFREE(z, key->heap, DYNAMIC_TYPE_DH);
        XFREE(x, key->heap, DYNAMIC_TYPE_DH);
    #endif
        XFREE(y, key->heap, DYNAMIC_TYPE_DH);
    #endif
        return ret;
    }
#endif

#if !defined(WOLFSSL_SP_MATH)
    if (mp_init_multi(x, y, z, 0, 0, 0) != MP_OKAY) {
    #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
        XFREE(z, key->heap, DYNAMIC_TYPE_DH);
        XFREE(x, key->heap, DYNAMIC_TYPE_DH);
        XFREE(y, key->heap, DYNAMIC_TYPE_DH);
    #endif
        return MP_INIT_E;
    }
#if defined(WOLFSSL_SP_MATH_ALL)
    if (ct) {
        /* TFM and Integer implementations keep high words zero.
         * SP math implementation needs all words set to zero as it doesn't
         * ensure unused words are zero. */
        mp_forcezero(x);
    }
#endif

    SAVE_VECTOR_REGISTERS(ret = _svr_ret;);

    if (mp_read_unsigned_bin(x, priv, privSz) != MP_OKAY)
        ret = MP_READ_E;
#ifdef WOLFSSL_CHECK_MEM_ZERO
    if (ret == 0)
        mp_memzero_add("wc_DhAgree_Sync x", x);
#endif

    if (ret == 0 && mp_read_unsigned_bin(y, otherPub, pubSz) != MP_OKAY)
        ret = MP_READ_E;

    if (ret == 0) {
        if (ct) {
            int bits;

            /* x is mod q but if q not available, use p (> q). */
            if (mp_iszero(&key->q) == MP_NO) {
                bits = mp_count_bits(&key->q);
            }
            else {
                bits = mp_count_bits(&key->p);
            }
            /* Exponentiate to the maximum words of a valid x to ensure a
             * constant time operation. */
            ret = mp_exptmod_ex(y, x, (bits + DIGIT_BIT - 1) / DIGIT_BIT,
                                &key->p, z);
        }
        else {
            ret = mp_exptmod(y, x, &key->p, z);
        }
        if (ret != MP_OKAY)
            ret = MP_EXPTMOD_E;
    }

#ifdef WOLFSSL_CHECK_MEM_ZERO
    if (ret == 0)
        mp_memzero_add("wc_DhAgree_Sync z", z);
#endif

    /* make sure z is not one (SP800-56A, 5.7.1.1) */
    if (ret == 0 && (mp_cmp_d(z, 1) == MP_EQ))
        ret = MP_VAL;

    if (ret == 0) {
        if (ct) {
            /* Put the secret into a buffer in constant time. */
            ret = mp_to_unsigned_bin_len_ct(z, agree, (int)*agreeSz);
        }
        else {
            ret = mp_to_unsigned_bin(z, agree);
            if (ret == MP_OKAY)
                *agreeSz = (word32)mp_unsigned_bin_size(z);
        }
    }

    mp_forcezero(z);
    mp_clear(y);
    mp_forcezero(x);

    RESTORE_VECTOR_REGISTERS();

#else
    (void)ct;
    ret = WC_KEY_SIZE_E;
#endif

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
#if !defined(WOLFSSL_SP_MATH)
    XFREE(z, key->heap, DYNAMIC_TYPE_DH);
    XFREE(x, key->heap, DYNAMIC_TYPE_DH);
#endif
    XFREE(y, key->heap, DYNAMIC_TYPE_DH);
#elif defined(WOLFSSL_CHECK_MEM_ZERO)
#if !defined(WOLFSSL_SP_MATH)
    mp_memzero_check(x);
    mp_memzero_check(z);
#endif
#endif

    return ret;
}

#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_DH)
static int wc_DhAgree_Async(DhKey* key, byte* agree, word32* agreeSz,
    const byte* priv, word32 privSz, const byte* otherPub, word32 pubSz)
{
    int ret;

#if defined(HAVE_INTEL_QA)
    word32 pBits;

    /* QAT DH sizes: 768, 1024, 1536, 2048, 3072 and 4096 bits */
    pBits = mp_unsigned_bin_size(&key->p) * 8;
    if (pBits == 768 ||  pBits == 1024 || pBits == 1536 ||
        pBits == 2048 || pBits == 3072 || pBits == 4096) {
        ret = wc_mp_to_bigint(&key->p, &key->p.raw);
        if (ret == MP_OKAY)
            ret = IntelQaDhAgree(&key->asyncDev, &key->p.raw,
                agree, agreeSz, priv, privSz, otherPub, pubSz);
        return ret;
    }

#elif defined(HAVE_CAVIUM)
    /* TODO: Not implemented - use software for now */

#elif defined(WOLFSSL_ASYNC_CRYPT_SW)
    if (wc_AsyncSwInit(&key->asyncDev, ASYNC_SW_DH_AGREE)) {
        WC_ASYNC_SW* sw = &key->asyncDev.sw;
        sw->dhAgree.key = key;
        sw->dhAgree.agree = agree;
        sw->dhAgree.agreeSz = agreeSz;
        sw->dhAgree.priv = priv;
        sw->dhAgree.privSz = privSz;
        sw->dhAgree.otherPub = otherPub;
        sw->dhAgree.pubSz = pubSz;
        return WC_PENDING_E;
    }
#endif

    /* otherwise use software DH */
    ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz,
                          0);

    return ret;
}
#endif /* WOLFSSL_ASYNC_CRYPT */
#endif /* !WOLFSSL_KCAPI_DH */

int wc_DhAgree(DhKey* key, byte* agree, word32* agreeSz, const byte* priv,
            word32 privSz, const byte* otherPub, word32 pubSz)
{
    int ret = 0;

    if (key == NULL || agree == NULL || agreeSz == NULL || priv == NULL ||
                                                            otherPub == NULL) {
        return BAD_FUNC_ARG;
    }

#ifdef WOLFSSL_KCAPI_DH
    (void)priv;
    (void)privSz;
    ret = KcapiDh_SharedSecret(key, otherPub, pubSz, agree, agreeSz);
#else
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_DH)
    /* Async marker takes precedence: when wolfAsync_DoSw (wolfcrypt/src/
     * async.c) re-enters the compute path, wc_DhAgree_Async dispatches
     * to the SP nonblock wrapper if key->nb is attached, and per-yield
     * FP_WOULDBLOCK (alias of MP_WOULDBLOCK) is translated to
     * WC_PENDING_E by wolfAsync_DoSw so the TLS event loop drives it. */
    if (key->asyncDev.marker == WOLFSSL_ASYNC_MARKER_DH) {
        ret = wc_DhAgree_Async(key, agree, agreeSz, priv, privSz, otherPub,
                               pubSz);
    }
    else
#endif
    {
        /* wc_DhAgree_Sync handles key->nb internally; no separate dispatch
         * needed here. wc_DhAgree_ct (constant-time fold-back) bypasses
         * this function entirely so passing ct=0 is correct. */
        ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub,
                              pubSz, 0);
    }
#endif /* WOLFSSL_KCAPI_DH */

    return ret;
}

int wc_DhAgree_ct(DhKey* key, byte* agree, word32 *agreeSz, const byte* priv,
            word32 privSz, const byte* otherPub, word32 pubSz)
{
    word32 requested_agreeSz;

    if (key == NULL || agree == NULL || agreeSz == NULL || priv == NULL ||
                                                            otherPub == NULL) {
        return BAD_FUNC_ARG;
    }

    requested_agreeSz = (word32)mp_unsigned_bin_size(&key->p);
    if (requested_agreeSz > *agreeSz) {
        return BUFFER_E;
    }
    *agreeSz = requested_agreeSz;

    return wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz,
                           1);
}

#ifdef WOLFSSL_DH_EXTRA
WOLFSSL_LOCAL int wc_DhKeyCopy(DhKey* src, DhKey* dst)
{
    int ret;

    if (!src || !dst || src == dst) {
        WOLFSSL_MSG("Parameters not provided or are the same");
        return BAD_FUNC_ARG;
    }

    if ((ret = mp_copy(&src->p, &dst->p)) != MP_OKAY) {
        WOLFSSL_MSG("mp_copy error");
        return ret;
    }

    if ((ret = mp_copy(&src->g, &dst->g)) != MP_OKAY) {
        WOLFSSL_MSG("mp_copy error");
        return ret;
    }

    if ((ret = mp_copy(&src->q, &dst->q)) != MP_OKAY) {
        WOLFSSL_MSG("mp_copy error");
        return ret;
    }

    if ((ret = mp_copy(&src->pub, &dst->pub)) != MP_OKAY) {
        WOLFSSL_MSG("mp_copy error");
        return ret;
    }

    if ((ret = mp_copy(&src->priv, &dst->priv)) != MP_OKAY) {
        WOLFSSL_MSG("mp_copy error");
        return ret;
    }
#ifdef WOLFSSL_CHECK_MEM_ZERO
    mp_memzero_add("wc_DhKeyCopy dst->priv", &dst->priv);
#endif

    dst->heap = src->heap;

    return MP_OKAY;
}

/* Sets private and public key in DhKey if both are available, otherwise sets
    either private or public key, depending on which is available. */
int wc_DhImportKeyPair(DhKey* key, const byte* priv, word32 privSz,
                       const byte* pub, word32 pubSz)
{
    byte havePriv, havePub;

    if (key == NULL) {
        return BAD_FUNC_ARG;
    }

    havePriv = ( (priv != NULL) && (privSz > 0) );
    havePub  = ( (pub  != NULL) && (pubSz  > 0) );

    if (!havePub && !havePriv) {
        WOLFSSL_MSG("No Public or Private Key to Set");
        return BAD_FUNC_ARG;
    }

    /* Set Private Key */
    if (havePriv) {
        /* may have leading 0 */
        if (priv[0] == 0) {
            privSz--; priv++;
        }
        if (mp_init(&key->priv) != MP_OKAY)
            havePriv = 0;
    }
    if (havePriv) {
        if (mp_read_unsigned_bin(&key->priv, priv, privSz) != MP_OKAY) {
            mp_forcezero(&key->priv);
            havePriv = 0;
        } else {
            WOLFSSL_MSG("DH Private Key Set");
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            mp_memzero_add("wc_DhImportKeyPair key->priv", &key->priv);
        #endif
        }
    }

    /* Set Public Key */
    if (havePub) {
        /* may have leading 0 */
        if (pub[0] == 0) {
            pubSz--; pub++;
        }
        if (mp_init(&key->pub) != MP_OKAY)
            havePub = 0;
    }
    if (havePub) {
        if (mp_read_unsigned_bin(&key->pub, pub, pubSz) != MP_OKAY) {
            mp_clear(&key->pub);
            havePub = 0;
            if (havePriv) {
                mp_forcezero(&key->priv);
                havePriv = 0; /* set to 0 to error out with failed read pub */
            }
        } else {
            WOLFSSL_MSG("DH Public Key Set");
        }
    }

    if (havePriv == 0 && havePub == 0) {
        return MEMORY_E;
    }

    return 0;
}

/* Can be used with WOLFSSL_DH_EXTRA when key is loaded with
    wc_DhKeyDecode or wc_DhImportKeyPair */
int wc_DhExportKeyPair(DhKey* key, byte* priv, word32* pPrivSz,
    byte* pub, word32* pPubSz)
{
    int ret = 0;

    if (key == NULL || (priv && pPrivSz == NULL) || (pub && pPubSz == NULL)) {
        return BAD_FUNC_ARG;
    }

    if (priv) {
        word32 privSz = (word32)mp_unsigned_bin_size(&key->priv);
        if (privSz > *pPrivSz) {
            return BUFFER_E;
        }
        *pPrivSz = privSz;
        ret |= mp_to_unsigned_bin(&key->priv, priv);
    }

    if (pub) {
        word32 pubSz = (word32)mp_unsigned_bin_size(&key->pub);
        if (pubSz > *pPubSz) {
            return BUFFER_E;
        }
        *pPubSz = pubSz;
        ret |= mp_to_unsigned_bin(&key->pub,  pub);
    }

    if (ret != 0)
        ret = ASN_DH_KEY_E;
    return ret;
}

#endif /* WOLFSSL_DH_EXTRA */

static int _DhSetKey(DhKey* key, const byte* p, word32 pSz, const byte* g,
                     word32 gSz, const byte* q, word32 qSz, int trusted,
                     WC_RNG* rng)
{
    int ret = 0;
    mp_int* keyP = NULL;
    mp_int* keyG = NULL;

    if (key == NULL || p == NULL || g == NULL || pSz == 0 || gSz == 0) {
        ret = BAD_FUNC_ARG;
    }

    SAVE_VECTOR_REGISTERS(return _svr_ret;);

    if (ret == 0) {
        /* may have leading 0 */
        if (p[0] == 0) {
            pSz--; p++;
        }

        if (g[0] == 0) {
            gSz--; g++;
        }

        if (q != NULL) {
            if (q[0] == 0) {
                qSz--; q++;
            }
        }

        if (mp_init(&key->p) != MP_OKAY)
            ret = MP_INIT_E;
    }

    if (ret == 0) {
        if (mp_read_unsigned_bin(&key->p, p, pSz) != MP_OKAY)
            ret = ASN_DH_KEY_E;
        else
            keyP = &key->p;
    }

    if (ret == 0 && !trusted) {
        int isPrime = 0;

        /* Short-circuit the primality check for p if it is one of the named
         * public moduli (known primes) from RFC 7919.
         */
        #ifdef HAVE_FFDHE_2048
        if ((pSz == sizeof(dh_ffdhe2048_p)) &&
            (XMEMCMP(p, dh_ffdhe2048_p, sizeof(dh_ffdhe2048_p)) == 0))
        {
            isPrime = 1;
        }
        else
        #endif
        #ifdef HAVE_FFDHE_3072
        if ((pSz == sizeof(dh_ffdhe3072_p)) &&
            (XMEMCMP(p, dh_ffdhe3072_p, sizeof(dh_ffdhe3072_p)) == 0))
        {
            isPrime = 1;
        }
        else
        #endif
        #ifdef HAVE_FFDHE_4096
        if ((pSz == sizeof(dh_ffdhe4096_p)) &&
            (XMEMCMP(p, dh_ffdhe4096_p, sizeof(dh_ffdhe4096_p)) == 0))
        {
            isPrime = 1;
        }
        else
        #endif
        #ifdef HAVE_FFDHE_6144
        if ((pSz == sizeof(dh_ffdhe6144_p)) &&
            (XMEMCMP(p, dh_ffdhe6144_p, sizeof(dh_ffdhe6144_p)) == 0))
        {
            isPrime = 1;
        }
        else
        #endif
        #ifdef HAVE_FFDHE_8192
        if ((pSz == sizeof(dh_ffdhe8192_p)) &&
            (XMEMCMP(p, dh_ffdhe8192_p, sizeof(dh_ffdhe8192_p)) == 0))
        {
            isPrime = 1;
        }
        else
        #endif
        {
            if (rng != NULL)
                ret = mp_prime_is_prime_ex(keyP, 8, &isPrime, rng);
            else
                ret = mp_prime_is_prime(keyP, 8, &isPrime);
        }

        if (ret == 0 && isPrime == 0)
            ret = DH_CHECK_PUB_E;
    }

    if (ret == 0 && mp_init(&key->g) != MP_OKAY)
        ret = MP_INIT_E;
    if (ret == 0) {
        if (mp_read_unsigned_bin(&key->g, g, gSz) != MP_OKAY)
            ret = ASN_DH_KEY_E;
        else
            keyG = &key->g;
    }

    if (ret == 0 && q != NULL) {
        if (mp_init(&key->q) != MP_OKAY)
            ret = MP_INIT_E;
    }
    if (ret == 0 && q != NULL) {
        if (mp_read_unsigned_bin(&key->q, q, qSz) != MP_OKAY)
            ret = MP_INIT_E;
        else
            key->trustedGroup = trusted;
    }

    if (ret != 0 && key != NULL) {
        if (keyG)
            mp_clear(keyG);
        if (keyP)
            mp_clear(keyP);
    }

    RESTORE_VECTOR_REGISTERS();

    return ret;
}


int wc_DhSetCheckKey(DhKey* key, const byte* p, word32 pSz, const byte* g,
                   word32 gSz, const byte* q, word32 qSz, int trusted,
                   WC_RNG* rng)
{
    return _DhSetKey(key, p, pSz, g, gSz, q, qSz, trusted, rng);
}


int wc_DhSetKey_ex(DhKey* key, const byte* p, word32 pSz, const byte* g,
                   word32 gSz, const byte* q, word32 qSz)
{
    return _DhSetKey(key, p, pSz, g, gSz, q, qSz, 0, NULL);
}


/* not in asn anymore since no actual asn types used */
int wc_DhSetKey(DhKey* key, const byte* p, word32 pSz, const byte* g,
                word32 gSz)
{
    /* This should not have trusted set. */
    return _DhSetKey(key, p, pSz, g, gSz, NULL, 0, 1, NULL);
}


int wc_DhSetNamedKey(DhKey* key, int name)
{
    const byte* p = NULL;
    const byte* g = NULL;
    const byte* q = NULL;
    word32 pSz = 0, gSz = 0, qSz = 0;

    switch (name) {
        #ifdef HAVE_FFDHE_2048
        case WC_FFDHE_2048:
            p = dh_ffdhe2048_p;
            pSz = sizeof(dh_ffdhe2048_p);
            g = dh_ffdhe2048_g;
            gSz = sizeof(dh_ffdhe2048_g);
            #ifdef HAVE_FFDHE_Q
            q = dh_ffdhe2048_q;
            qSz = sizeof(dh_ffdhe2048_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_2048 */
        #ifdef HAVE_FFDHE_3072
        case WC_FFDHE_3072:
            p = dh_ffdhe3072_p;
            pSz = sizeof(dh_ffdhe3072_p);
            g = dh_ffdhe3072_g;
            gSz = sizeof(dh_ffdhe3072_g);
            #ifdef HAVE_FFDHE_Q
            q = dh_ffdhe3072_q;
            qSz = sizeof(dh_ffdhe3072_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_3072 */
        #ifdef HAVE_FFDHE_4096
        case WC_FFDHE_4096:
            p = dh_ffdhe4096_p;
            pSz = sizeof(dh_ffdhe4096_p);
            g = dh_ffdhe4096_g;
            gSz = sizeof(dh_ffdhe4096_g);
            #ifdef HAVE_FFDHE_Q
            q = dh_ffdhe4096_q;
            qSz = sizeof(dh_ffdhe4096_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_4096 */
        #ifdef HAVE_FFDHE_6144
        case WC_FFDHE_6144:
            p = dh_ffdhe6144_p;
            pSz = sizeof(dh_ffdhe6144_p);
            g = dh_ffdhe6144_g;
            gSz = sizeof(dh_ffdhe6144_g);
            #ifdef HAVE_FFDHE_Q
            q = dh_ffdhe6144_q;
            qSz = sizeof(dh_ffdhe6144_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_6144 */
        #ifdef HAVE_FFDHE_8192
        case WC_FFDHE_8192:
            p = dh_ffdhe8192_p;
            pSz = sizeof(dh_ffdhe8192_p);
            g = dh_ffdhe8192_g;
            gSz = sizeof(dh_ffdhe8192_g);
            #ifdef HAVE_FFDHE_Q
            q = dh_ffdhe8192_q;
            qSz = sizeof(dh_ffdhe8192_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_8192 */
        default:
            break;
    }
    return _DhSetKey(key, p, pSz, g, gSz, q, qSz, 1, NULL);
}


word32 wc_DhGetNamedKeyMinSize(int name)
{
    word32 size;

    switch (name) {
        #ifdef HAVE_FFDHE_2048
        case WC_FFDHE_2048:
            size = 29;
            break;
        #endif /* HAVE_FFDHE_2048 */
        #ifdef HAVE_FFDHE_3072
        case WC_FFDHE_3072:
            size = 34;
            break;
        #endif /* HAVE_FFDHE_3072 */
        #ifdef HAVE_FFDHE_4096
        case WC_FFDHE_4096:
            size = 39;
            break;
        #endif /* HAVE_FFDHE_4096 */
        #ifdef HAVE_FFDHE_6144
        case WC_FFDHE_6144:
            size = 46;
            break;
        #endif /* HAVE_FFDHE_6144 */
        #ifdef HAVE_FFDHE_8192
        case WC_FFDHE_8192:
            size = 52;
            break;
        #endif /* HAVE_FFDHE_8192 */
        default:
            size = 0;
    }

    return size;
}


/* Returns 1: params match
 *         0: params differ */
int wc_DhCmpNamedKey(int name, int noQ,
        const byte* p, word32 pSz,
        const byte* g, word32 gSz,
        const byte* q, word32 qSz)
{
    const byte* pCmp = NULL;
    const byte* qCmp = NULL;
    const byte* gCmp = NULL;
    word32 pCmpSz = 0, qCmpSz = 0, gCmpSz = 0;
    int cmp = 0, goodName = 1;

    switch (name) {
        #ifdef HAVE_FFDHE_2048
        case WC_FFDHE_2048:
            pCmp = dh_ffdhe2048_p;
            pCmpSz = sizeof(dh_ffdhe2048_p);
            gCmp = dh_ffdhe2048_g;
            gCmpSz = sizeof(dh_ffdhe2048_g);
            #ifdef HAVE_FFDHE_Q
            qCmp = dh_ffdhe2048_q;
            qCmpSz = sizeof(dh_ffdhe2048_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_2048 */
        #ifdef HAVE_FFDHE_3072
        case WC_FFDHE_3072:
            pCmp = dh_ffdhe3072_p;
            pCmpSz = sizeof(dh_ffdhe3072_p);
            gCmp = dh_ffdhe3072_g;
            gCmpSz = sizeof(dh_ffdhe3072_g);
            #ifdef HAVE_FFDHE_Q
            qCmp = dh_ffdhe3072_q;
            qCmpSz = sizeof(dh_ffdhe3072_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_3072 */
        #ifdef HAVE_FFDHE_4096
        case WC_FFDHE_4096:
            pCmp = dh_ffdhe4096_p;
            pCmpSz = sizeof(dh_ffdhe4096_p);
            gCmp = dh_ffdhe4096_g;
            gCmpSz = sizeof(dh_ffdhe4096_g);
            #ifdef HAVE_FFDHE_Q
            qCmp = dh_ffdhe4096_q;
            qCmpSz = sizeof(dh_ffdhe4096_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_4096 */
        #ifdef HAVE_FFDHE_6144
        case WC_FFDHE_6144:
            pCmp = dh_ffdhe6144_p;
            pCmpSz = sizeof(dh_ffdhe6144_p);
            gCmp = dh_ffdhe6144_g;
            gCmpSz = sizeof(dh_ffdhe6144_g);
            #ifdef HAVE_FFDHE_Q
            qCmp = dh_ffdhe6144_q;
            qCmpSz = sizeof(dh_ffdhe6144_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_6144 */
        #ifdef HAVE_FFDHE_8192
        case WC_FFDHE_8192:
            pCmp = dh_ffdhe8192_p;
            pCmpSz = sizeof(dh_ffdhe8192_p);
            gCmp = dh_ffdhe8192_g;
            gCmpSz = sizeof(dh_ffdhe8192_g);
            #ifdef HAVE_FFDHE_Q
            qCmp = dh_ffdhe8192_q;
            qCmpSz = sizeof(dh_ffdhe8192_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_8192 */
        default:
            goodName = 0;
    }

    if (goodName) {
        cmp = (pSz == pCmpSz) && (gSz == gCmpSz) &&
            (noQ || ((qCmp != NULL) && (qSz == qCmpSz) &&
                     XMEMCMP(q, qCmp, qCmpSz) == 0)) &&
            (XMEMCMP(p, pCmp, pCmpSz) == 0) &&
            (XMEMCMP(g, gCmp, gCmpSz) == 0);
    }

    return cmp;
}


int wc_DhGetNamedKeyParamSize(int name, word32* p, word32* g, word32* q)
{
    word32 pSz = 0, gSz = 0, qSz = 0;

    switch (name) {
        #ifdef HAVE_FFDHE_2048
        case WC_FFDHE_2048:
            pSz = sizeof(dh_ffdhe2048_p);
            gSz = sizeof(dh_ffdhe2048_g);
            #ifdef HAVE_FFDHE_Q
            qSz = sizeof(dh_ffdhe2048_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_2048 */
        #ifdef HAVE_FFDHE_3072
        case WC_FFDHE_3072:
            pSz = sizeof(dh_ffdhe3072_p);
            gSz = sizeof(dh_ffdhe3072_g);
            #ifdef HAVE_FFDHE_Q
            qSz = sizeof(dh_ffdhe3072_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_3072 */
        #ifdef HAVE_FFDHE_4096
        case WC_FFDHE_4096:
            pSz = sizeof(dh_ffdhe4096_p);
            gSz = sizeof(dh_ffdhe4096_g);
            #ifdef HAVE_FFDHE_Q
            qSz = sizeof(dh_ffdhe4096_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_4096 */
        #ifdef HAVE_FFDHE_6144
        case WC_FFDHE_6144:
            pSz = sizeof(dh_ffdhe6144_p);
            gSz = sizeof(dh_ffdhe6144_g);
            #ifdef HAVE_FFDHE_Q
            qSz = sizeof(dh_ffdhe6144_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_6144 */
        #ifdef HAVE_FFDHE_8192
        case WC_FFDHE_8192:
            pSz = sizeof(dh_ffdhe8192_p);
            gSz = sizeof(dh_ffdhe8192_g);
            #ifdef HAVE_FFDHE_Q
            qSz = sizeof(dh_ffdhe8192_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_8192 */
        default:
            break;
    }

    if (p != NULL) *p = pSz;
    if (g != NULL) *g = gSz;
    if (q != NULL) *q = qSz;

    return 0;
}


int wc_DhCopyNamedKey(int name,
        byte* p, word32* pSz, byte* g, word32* gSz, byte* q, word32* qSz)
{
    const byte* pC = NULL;
    const byte* gC = NULL;
    const byte* qC = NULL;
    word32 pCSz = 0, gCSz = 0, qCSz = 0;

    switch (name) {
        #ifdef HAVE_FFDHE_2048
        case WC_FFDHE_2048:
            pC = dh_ffdhe2048_p;
            pCSz = sizeof(dh_ffdhe2048_p);
            gC = dh_ffdhe2048_g;
            gCSz = sizeof(dh_ffdhe2048_g);
            #ifdef HAVE_FFDHE_Q
            qC = dh_ffdhe2048_q;
            qCSz = sizeof(dh_ffdhe2048_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_2048 */
        #ifdef HAVE_FFDHE_3072
        case WC_FFDHE_3072:
            pC = dh_ffdhe3072_p;
            pCSz = sizeof(dh_ffdhe3072_p);
            gC = dh_ffdhe3072_g;
            gCSz = sizeof(dh_ffdhe3072_g);
            #ifdef HAVE_FFDHE_Q
            qC = dh_ffdhe3072_q;
            qCSz = sizeof(dh_ffdhe3072_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_3072 */
        #ifdef HAVE_FFDHE_4096
        case WC_FFDHE_4096:
            pC = dh_ffdhe4096_p;
            pCSz = sizeof(dh_ffdhe4096_p);
            gC = dh_ffdhe4096_g;
            gCSz = sizeof(dh_ffdhe4096_g);
            #ifdef HAVE_FFDHE_Q
            qC = dh_ffdhe4096_q;
            qCSz = sizeof(dh_ffdhe4096_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_4096 */
        #ifdef HAVE_FFDHE_6144
        case WC_FFDHE_6144:
            pC = dh_ffdhe6144_p;
            pCSz = sizeof(dh_ffdhe6144_p);
            gC = dh_ffdhe6144_g;
            gCSz = sizeof(dh_ffdhe6144_g);
            #ifdef HAVE_FFDHE_Q
            qC = dh_ffdhe6144_q;
            qCSz = sizeof(dh_ffdhe6144_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_6144 */
        #ifdef HAVE_FFDHE_8192
        case WC_FFDHE_8192:
            pC = dh_ffdhe8192_p;
            pCSz = sizeof(dh_ffdhe8192_p);
            gC = dh_ffdhe8192_g;
            gCSz = sizeof(dh_ffdhe8192_g);
            #ifdef HAVE_FFDHE_Q
            qC = dh_ffdhe8192_q;
            qCSz = sizeof(dh_ffdhe8192_q);
            #endif /* HAVE_FFDHE_Q */
            break;
        #endif /* HAVE_FFDHE_8192 */
        default:
            break;
    }

    if (p != NULL && pC != NULL)
        XMEMCPY(p, pC, pCSz);
    if (pSz != NULL)
        *pSz = pCSz;
    if (g != NULL && gC != NULL)
        XMEMCPY(g, gC, gCSz);
    if (gSz != NULL)
        *gSz = gCSz;
    if (q != NULL && qC != NULL)
        XMEMCPY(q, qC, qCSz);
    if (qSz != NULL)
        *qSz = qCSz;

    return 0;
}


#ifdef WOLFSSL_KEY_GEN

/* modulus_size in bits */
int wc_DhGenerateParams(WC_RNG *rng, int modSz, DhKey *dh)
{
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    mp_int *tmp = NULL, *tmp2 = NULL;
#else
    mp_int tmp[1], tmp2[2];
#endif
    word32  groupSz = 0, bufSz = 0,
            primeCheckCount = 0;
    int     primeCheck = MP_NO,
            ret = 0;
#ifdef WOLFSSL_NO_MALLOC
    unsigned char buf[DH_MAX_SIZE / WOLFSSL_BIT_SIZE];
#else
    unsigned char *buf = NULL;
#endif

#if !defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)
    XMEMSET(tmp, 0, sizeof(tmp));
    XMEMSET(tmp2, 0, sizeof(tmp2));
#endif

    if (rng == NULL || dh == NULL)
        ret = BAD_FUNC_ARG;

    /* set group size in bytes from modulus size
     * FIPS 186-4 defines valid values (1024, 160) (2048, 256) (3072, 256)
     */
    if (ret == 0) {
        switch (modSz) {
            case 1024:
                groupSz = 20;
                break;
            case 2048:
            case 3072:
                groupSz = 32;
                break;
            default:
        #if !defined(HAVE_FIPS) && defined(WOLFSSL_NO_DH186)
                /* in non fips mode attempt to match strength of group size with
                 * mod size */
                if (modSz < 2048)
                    groupSz = 20;
                else
                    groupSz = 32;
        #else
                ret = BAD_FUNC_ARG;
        #endif
                break;
        }
    }

    if (ret == 0) {
        /* modulus size in bytes */
        modSz /= WOLFSSL_BIT_SIZE;

        if ((word32)modSz < groupSz) {
            WOLFSSL_MSG("DH modSz was too small");
            ret = BAD_FUNC_ARG;
        }
    }

    if (ret == 0) {
        bufSz = (word32)modSz - groupSz;

#ifdef WOLFSSL_NO_MALLOC
        if (bufSz > sizeof(buf))
            ret = MEMORY_E;
#else
        /* allocate ram */
        buf = (unsigned char *)XMALLOC(bufSz,
                                       dh->heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (buf == NULL)
            ret = MEMORY_E;
#endif
    }

    /* make a random string that will be multiplied against q */
    if (ret == 0)
        ret = wc_RNG_GenerateBlock(rng, buf, bufSz);

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    if (ret == 0) {
        if ((tmp = (mp_int *)XMALLOC(sizeof(*tmp), NULL,
                DYNAMIC_TYPE_WOLF_BIGINT)) == NULL) {
            ret = MEMORY_E;
        }
        else {
            XMEMSET(tmp, 0, sizeof(*tmp));
        }
    }
    if (ret == 0) {
        if ((tmp2 = (mp_int *)XMALLOC(sizeof(*tmp2), NULL,
                DYNAMIC_TYPE_WOLF_BIGINT)) == NULL) {
            ret = MEMORY_E;
        }
        else {
            XMEMSET(tmp2, 0, sizeof(*tmp2));
        }
    }
#endif

    SAVE_VECTOR_REGISTERS(ret = _svr_ret;);

    if (ret == 0) {
        /* force magnitude */
        buf[0] |= 0xC0;
        /* force even */
        buf[bufSz - 1] &= 0xfe;

        if (mp_init_multi(tmp, tmp2, &dh->p, &dh->q, &dh->g, 0)
                != MP_OKAY) {
            ret = MP_INIT_E;
        }
    }

    if (ret == 0) {
        if (mp_read_unsigned_bin(tmp2, buf, bufSz) != MP_OKAY)
            ret = MP_READ_E;
    }

    /* make our prime q */
    if (ret == 0) {
        if (mp_rand_prime(&dh->q, (int)groupSz, rng, NULL) != MP_OKAY)
            ret = PRIME_GEN_E;
    }

    /* p = random * q */
    if (ret == 0) {
        if (mp_mul(&dh->q, tmp2, &dh->p) != MP_OKAY)
            ret = MP_MUL_E;
    }

    /* p = random * q + 1, so q is a prime divisor of p-1 */
    if (ret == 0) {
        if (mp_add_d(&dh->p, 1, &dh->p) != MP_OKAY)
            ret = MP_ADD_E;
    }

    /* tmp = 2q  */
    if (ret == 0) {
        if (mp_add(&dh->q, &dh->q, tmp) != MP_OKAY)
            ret = MP_ADD_E;
    }

    /* loop until p is prime */
    if (ret == 0) {
        for (;;) {
            if (mp_prime_is_prime_ex(&dh->p, 8, &primeCheck, rng) != MP_OKAY)
                ret = PRIME_GEN_E;

            if (primeCheck != MP_YES) {
                /* p += 2q */
                if (mp_add(tmp, &dh->p, &dh->p) != MP_OKAY)
                    ret = MP_ADD_E;
                else
                    primeCheckCount++;
            }

            if (ret != 0 || primeCheck == MP_YES)
                break;

            /* linuxkm: release the kernel for a moment before iterating. */
            RESTORE_VECTOR_REGISTERS();
            SAVE_VECTOR_REGISTERS(ret = _svr_ret; break;);
        };
    }

    /* tmp2 += (2*loop_check_prime)
     * to have p = (q * tmp2) + 1 prime
     */
    if ((ret == 0) && (primeCheckCount)) {
        if (mp_add_d(tmp2, 2 * primeCheckCount, tmp2) != MP_OKAY)
            ret = MP_ADD_E;
    }

    /* find a value g for which g^tmp2 != 1 */
    if ((ret == 0) && (mp_set(&dh->g, 1) != MP_OKAY))
        ret = MP_ZERO_E;

    if (ret == 0) {
        do {
            if (mp_add_d(&dh->g, 1, &dh->g) != MP_OKAY)
                ret = MP_ADD_E;
            else if (mp_exptmod(&dh->g, tmp2, &dh->p, tmp) != MP_OKAY)
                ret = MP_EXPTMOD_E;
        } while (ret == 0 && mp_cmp_d(tmp, 1) == MP_EQ);
    }

    if (ret == 0) {
        /* at this point tmp generates a group of order q mod p */
#ifndef USE_FAST_MATH
        /* Exchanging is quick when the data pointer can be copied. */
        mp_exch(tmp, &dh->g);
#else
        mp_copy(tmp, &dh->g);
#endif
    }

    /* clear the parameters if there was an error */
    if ((ret != 0) && (dh != NULL)) {
        mp_clear(&dh->q);
        mp_clear(&dh->p);
        mp_clear(&dh->g);
    }

    RESTORE_VECTOR_REGISTERS();

#ifndef WOLFSSL_NO_MALLOC
    if (buf != NULL)
#endif
    {
        ForceZero(buf, bufSz);
#ifndef WOLFSSL_NO_MALLOC
        if (dh != NULL) {
            XFREE(buf, dh->heap, DYNAMIC_TYPE_TMP_BUFFER);
        }
#endif
    }

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    if (tmp != NULL) {
        mp_clear(tmp);
        XFREE(tmp, NULL, DYNAMIC_TYPE_WOLF_BIGINT);
    }
    if (tmp2 != NULL) {
        mp_clear(tmp2);
        XFREE(tmp2, NULL, DYNAMIC_TYPE_WOLF_BIGINT);
    }
#else
    mp_clear(tmp);
    mp_clear(tmp2);
#endif

    return ret;
}

#endif /* WOLFSSL_KEY_GEN */

/* Export raw DH parameters from DhKey structure
 *
 * dh   - pointer to initialized DhKey structure
 * p    - output location for DH (p) parameter
 * pSz  - [IN/OUT] size of output buffer for p, size of p
 * q    - output location for DH (q) parameter
 * qSz  - [IN/OUT] size of output buffer for q, size of q
 * g    - output location for DH (g) parameter
 * gSz  - [IN/OUT] size of output buffer for g, size of g
 *
 * If p, q, and g pointers are all passed in as NULL, the function
 * will set pSz, qSz, and gSz to the required output buffer sizes for p,
 * q, and g. In this case, the function will return LENGTH_ONLY_E.
 *
 * returns 0 on success, negative upon failure
 */
int wc_DhExportParamsRaw(DhKey* dh, byte* p, word32* pSz,
                         byte* q, word32* qSz, byte* g, word32* gSz)
{
    int ret = 0;
    word32 pLen = 0, qLen = 0, gLen = 0;

    if (dh == NULL || pSz == NULL || qSz == NULL || gSz == NULL)
        ret = BAD_FUNC_ARG;

    /* get required output buffer sizes */
    if (ret == 0) {
        pLen = (word32)mp_unsigned_bin_size(&dh->p);
        qLen = (word32)mp_unsigned_bin_size(&dh->q);
        gLen = (word32)mp_unsigned_bin_size(&dh->g);

        /* return buffer sizes and LENGTH_ONLY_E if buffers are NULL */
        if (p == NULL && q == NULL && g == NULL) {
            *pSz = pLen;
            *qSz = qLen;
            *gSz = gLen;
            ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
        }
    }

    if (ret == 0) {
        if (p == NULL || q == NULL || g == NULL)
            ret = BAD_FUNC_ARG;
    }

    /* export p */
    if (ret == 0) {
        if (*pSz < pLen) {
            WOLFSSL_MSG("Output buffer for DH p parameter too small, "
                        "required size placed into pSz");
            *pSz = pLen;
            ret = BUFFER_E;
        }
    }

    if (ret == 0) {
        *pSz = pLen;
        if (mp_to_unsigned_bin(&dh->p, p) != MP_OKAY)
            ret = MP_TO_E;
    }

    /* export q */
    if (ret == 0) {
        if (*qSz < qLen) {
            WOLFSSL_MSG("Output buffer for DH q parameter too small, "
                        "required size placed into qSz");
            *qSz = qLen;
            ret = BUFFER_E;
        }
    }

    if (ret == 0) {
        *qSz = qLen;
        if (mp_to_unsigned_bin(&dh->q, q) != MP_OKAY)
            ret = MP_TO_E;
    }

    /* export g */
    if (ret == 0) {
        if (*gSz < gLen) {
            WOLFSSL_MSG("Output buffer for DH g parameter too small, "
                        "required size placed into gSz");
            *gSz = gLen;
            ret = BUFFER_E;
        }
    }

    if (ret == 0) {
        *gSz = gLen;
        if (mp_to_unsigned_bin(&dh->g, g) != MP_OKAY)
            ret = MP_TO_E;
    }

    return ret;
}

#endif /* NO_DH */
