/* dh.c
 *
 * Copyright (C) 2006-2019 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 2 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
 */


#ifdef HAVE_CONFIG_H
    #include <config.h>
#endif

#include <wolfssl/wolfcrypt/settings.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$m")
        #pragma const_seg(".fipsB$m")
    #endif
#endif

#include <wolfssl/wolfcrypt/dh.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/logging.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


/*
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

*/


#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 */

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

#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 */

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

#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 */

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

#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 */

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

#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 */

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

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 */

    if (mp_init_multi(&key->p, &key->g, &key->q, NULL, NULL, NULL) != MP_OKAY)
        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

    return ret;
}

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


int wc_FreeDhKey(DhKey* key)
{
    if (key) {
        mp_clear(&key->p);
        mp_clear(&key->g);
        mp_clear(&key->q);

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


#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)(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 unsual size
   otherwise round up on size needed */
#ifndef WOLFSSL_DH_CONST
    #define WOLFSSL_DH_ROUND(x)
#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(int modLen, int 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;
        default:
            break;
    }

    return ret;
}


/* Create DH private key
 *
 * Based on NIST FIPS 186-4,
 * "B.1.1 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)
{
    byte* cBuf;
    int qSz, pSz, cSz, err;
#ifdef WOLFSSL_SMALL_STACK
    mp_int* tmpQ = NULL;
    mp_int* tmpX = NULL;
#else
    mp_int tmpQ[1], tmpX[1];
#endif

    /* Parameters validated in calling functions. */

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

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

    /* verify (L,N) pair bit lengths */
    if (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 = qSz + (64 / WOLFSSL_BIT_SIZE);
    cBuf = (byte*)XMALLOC(cSz, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (cBuf == NULL) {
        return MEMORY_E;
    }
#ifdef WOLFSSL_SMALL_STACK
    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) {
        XFREE(cBuf, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
#ifdef WOLFSSL_SMALL_STACK
        XFREE(tmpQ, key->heap, DYNAMIC_TYPE_DH);
        XFREE(tmpX, key->heap, DYNAMIC_TYPE_DH);
#endif
        return err;
    }

    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_clear(tmpX);
            mp_clear(tmpQ);
            XFREE(cBuf, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
#ifdef WOLFSSL_SMALL_STACK
            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);
    XFREE(cBuf, key->heap, DYNAMIC_TYPE_TMP_BUFFER);

    /* tmpQ = q - 1 */
    if (err == MP_OKAY)
        err = mp_copy(&key->q, tmpQ);

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

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

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

    /* copy tmpX into priv */
    if (err == MP_OKAY) {
        pSz = mp_unsigned_bin_size(tmpX);
        if (pSz > (int)*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(tmpX);
    mp_clear(tmpQ);
#ifdef WOLFSSL_SMALL_STACK
    XFREE(tmpQ, key->heap, DYNAMIC_TYPE_DH);
    XFREE(tmpX, key->heap, DYNAMIC_TYPE_DH);
#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;

#ifndef WOLFSSL_NO_DH186
    if (mp_iszero(&key->q) == MP_NO) {

        /* q param available, use NIST FIPS 186-4, "B.1.1 Key Pair
         * Generation Using Extra Random Bits" */
        ret = GeneratePrivateDh186(key, rng, priv, privSz);

    } else
#endif
    {

        sz = 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
        }

        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
#ifdef WOLFSSL_SMALL_STACK
    mp_int* x = NULL;
    mp_int* y = NULL;
#else
    mp_int x[1];
    mp_int y[1];
#endif
#endif

#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

#ifndef WOLFSSL_SP_MATH
#ifdef WOLFSSL_SMALL_STACK
    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) {
    #ifdef WOLFSSL_SMALL_STACK
        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 = mp_unsigned_bin_size(y);

    mp_clear(y);
    mp_clear(x);
#ifdef WOLFSSL_SMALL_STACK
    XFREE(y, key->heap, DYNAMIC_TYPE_DH);
    XFREE(x, key->heap, DYNAMIC_TYPE_DH);
#endif
#else
    ret = WC_KEY_SIZE_E;
#endif

    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;
    }

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

    return (ret != 0) ? ret : GeneratePublicDh(key, priv, *privSz, pub, pubSz);
}

#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_clear(&x);

        return ret;
    }

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

#else /* WOLFSSL_ASYNC_CRYPT_TEST */
    if (wc_AsyncTestInit(&key->asyncDev, ASYNC_TEST_DH_GEN)) {
        WC_ASYNC_TEST* testDev = &key->asyncDev.test;
        testDev->dhGen.key = key;
        testDev->dhGen.rng = rng;
        testDev->dhGen.priv = priv;
        testDev->dhGen.privSz = privSz;
        testDev->dhGen.pub = pub;
        testDev->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).
 * Check per process in SP 800-56Ar3, section 5.6.2.3.1.
 *
 * 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
 *
 *  returns 0 on success or error code
 */
int wc_DhCheckPubKey_ex(DhKey* key, const byte* pub, word32 pubSz,
                        const byte* prime, word32 primeSz)
{
    int ret = 0;
#ifdef WOLFSSL_SMALL_STACK
    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;
    }

#ifdef WOLFSSL_SMALL_STACK
    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) {
    #ifdef WOLFSSL_SMALL_STACK
        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;
    }

    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.1, process step 1 */
    /* 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 (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;
    }

    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_NO_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

        {
    /* SP 800-56Ar3, section 5.6.2.3.1, process step 2 */
#ifndef WOLFSSL_SP_MATH
            /* calculate (y^q) mod(p), store back into y */
            if (ret == 0 && 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);
#ifdef WOLFSSL_SMALL_STACK
    XFREE(q, key->heap, DYNAMIC_TYPE_DH);
    XFREE(p, key->heap, DYNAMIC_TYPE_DH);
    XFREE(y, key->heap, DYNAMIC_TYPE_DH);
#endif

    return ret;
}


/* Check DH Public Key for invalid numbers
 *
 * 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 wc_DhCheckPubKey_ex(key, pub, pubSz, NULL, 0);
}


/**
 * Quick validity check of public key value agaist 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;
#ifdef WOLFSSL_SMALL_STACK
    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;
    }

#ifdef WOLFSSL_SMALL_STACK
    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) {
    #ifdef WOLFSSL_SMALL_STACK
        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) {
        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 (ret == 0) {
                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_clear(x);
    mp_clear(q);
#ifdef WOLFSSL_SMALL_STACK
    XFREE(q, key->heap, DYNAMIC_TYPE_DH);
    XFREE(x, key->heap, DYNAMIC_TYPE_DH);
#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);
}


/* 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)
{
#ifdef WOLFSSL_SMALL_STACK
    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;

#ifdef WOLFSSL_SMALL_STACK
    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) {

    #ifdef WOLFSSL_SMALL_STACK
        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;
    }

    /* 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;
    }

    /* 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
        {
#ifndef 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(privateKey);
    mp_clear(publicKey);
    mp_clear(checkKey);
#ifdef WOLFSSL_SMALL_STACK
    XFREE(checkKey, key->heap, DYNAMIC_TYPE_DH);
    XFREE(privateKey, key->heap, DYNAMIC_TYPE_DH);
    XFREE(publicKey, key->heap, DYNAMIC_TYPE_DH);
#endif

    return ret;
}


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;
    }

#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);
    }

    return ret;
}


static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz,
    const byte* priv, word32 privSz, const byte* otherPub, word32 pubSz)
{
    int ret = 0;
#ifdef WOLFSSL_SMALL_STACK
    mp_int* y = NULL;
#ifndef WOLFSSL_SP_MATH
    mp_int* x = NULL;
    mp_int* z = NULL;
#endif
#else
    mp_int y[1];
#ifndef WOLFSSL_SP_MATH
    mp_int x[1];
    mp_int z[1];
#endif
#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;
    }

    if (wc_DhCheckPubKey(key, otherPub, pubSz) != 0) {
        WOLFSSL_MSG("wc_DhAgree wc_DhCheckPubKey failed");
        return DH_CHECK_PUB_E;
    }
#endif

#ifdef WOLFSSL_SMALL_STACK
    y = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH);
    if (y == NULL)
        return MEMORY_E;
#ifndef 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
#ifndef WOLFSSL_SP_NO_2048
    if (mp_count_bits(&key->p) == 2048) {
        if (mp_init(y) != MP_OKAY)
            return MP_INIT_E;

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

        if (ret == 0)
            ret = sp_DhExp_2048(y, priv, privSz, &key->p, agree, agreeSz);

        mp_clear(y);
    #ifdef WOLFSSL_SMALL_STACK
    #ifndef 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
#ifndef WOLFSSL_SP_NO_3072
    if (mp_count_bits(&key->p) == 3072) {
        if (mp_init(y) != MP_OKAY)
            return MP_INIT_E;

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

        if (ret == 0)
            ret = sp_DhExp_3072(y, priv, privSz, &key->p, agree, agreeSz);

        mp_clear(y);
    #ifdef WOLFSSL_SMALL_STACK
    #ifndef 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
#ifdef WOLFSSL_SP_4096
    if (mp_count_bits(&key->p) == 4096) {
        if (mp_init(y) != MP_OKAY)
            return MP_INIT_E;

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

        if (ret == 0)
            ret = sp_DhExp_4096(y, priv, privSz, &key->p, agree, agreeSz);

        mp_clear(y);
    #ifdef WOLFSSL_SMALL_STACK
    #ifndef 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
#endif

#ifndef WOLFSSL_SP_MATH
    if (mp_init_multi(x, y, z, 0, 0, 0) != MP_OKAY) {
    #ifdef WOLFSSL_SMALL_STACK
        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 (mp_read_unsigned_bin(x, priv, privSz) != MP_OKAY)
        ret = MP_READ_E;

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

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

    /* 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 && mp_to_unsigned_bin(z, agree) != MP_OKAY)
        ret = MP_TO_E;

    if (ret == 0)
        *agreeSz = mp_unsigned_bin_size(z);

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

#ifdef WOLFSSL_SMALL_STACK
#ifndef 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;
}

#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 */

#else /* WOLFSSL_ASYNC_CRYPT_TEST */
    if (wc_AsyncTestInit(&key->asyncDev, ASYNC_TEST_DH_AGREE)) {
        WC_ASYNC_TEST* testDev = &key->asyncDev.test;
        testDev->dhAgree.key = key;
        testDev->dhAgree.agree = agree;
        testDev->dhAgree.agreeSz = agreeSz;
        testDev->dhAgree.priv = priv;
        testDev->dhAgree.privSz = privSz;
        testDev->dhAgree.otherPub = otherPub;
        testDev->dhAgree.pubSz = pubSz;
        return WC_PENDING_E;
    }
#endif

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

    return ret;
}
#endif /* WOLFSSL_ASYNC_CRYPT */

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;
    }

#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_DH)
    if (key->asyncDev.marker == WOLFSSL_ASYNC_MARKER_DH) {
        ret = wc_DhAgree_Async(key, agree, agreeSz, priv, privSz, otherPub, pubSz);
    }
    else
#endif
    {
        ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz);
    }

    return ret;
}


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;
    mp_int* keyQ = NULL;

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

    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;
        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
            keyQ = &key->q;
    }

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

    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, 1, 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)
{
    return _DhSetKey(key, p, pSz, g, gSz, NULL, 0, 1, NULL);
}


#ifdef WOLFSSL_KEY_GEN

/* modulus_size in bits */
int wc_DhGenerateParams(WC_RNG *rng, int modSz, DhKey *dh)
{
    mp_int  tmp, tmp2;
    int     groupSz = 0, bufSz = 0,
            primeCheckCount = 0,
            primeCheck = MP_NO,
            ret = 0;
    unsigned char *buf = NULL;

    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:
                ret = BAD_FUNC_ARG;
                break;
        }
    }

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

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

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

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

        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, 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) {
        do {
            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++;
            }
        } while (ret == 0 && primeCheck == MP_NO);
    }

    /* 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 */
        mp_exch(&tmp, &dh->g);
    }

    /* 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);
    }

    if (buf != NULL) {
        ForceZero(buf, bufSz);
        if (dh != NULL) {
            XFREE(buf, dh->heap, DYNAMIC_TYPE_TMP_BUFFER);
        }
    }
    mp_clear(&tmp);
    mp_clear(&tmp2);

    return ret;
}


/* 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 = mp_unsigned_bin_size(&dh->p);
        qLen = mp_unsigned_bin_size(&dh->q);
        gLen = 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 = 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 /* WOLFSSL_KEY_GEN */

#endif /* NO_DH */
