@@ -972,6 +972,10 @@ int wc_InitDhKey_ex(DhKey* key, void* heap, int devId)
972972 key -> handle = NULL ;
973973#endif
974974
975+ #ifdef WC_DH_NONBLOCK
976+ key -> nb = NULL ;
977+ #endif
978+
975979 return ret ;
976980}
977981
@@ -980,6 +984,23 @@ int wc_InitDhKey(DhKey* key)
980984 return wc_InitDhKey_ex (key , NULL , INVALID_DEVID );
981985}
982986
987+ #ifdef WC_DH_NONBLOCK
988+ int wc_DhSetNonBlock (DhKey * key , DhNb * nb )
989+ {
990+ if (key == NULL )
991+ return BAD_FUNC_ARG ;
992+
993+ if (nb != NULL ) {
994+ XMEMSET (nb , 0 , sizeof (DhNb ));
995+ }
996+
997+ /* Pass NULL to disable non-blocking mode. */
998+ key -> nb = nb ;
999+
1000+ return 0 ;
1001+ }
1002+ #endif
1003+
9831004
9841005int wc_FreeDhKey (DhKey * key )
9851006{
@@ -2043,6 +2064,36 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz,
20432064 return DH_CHECK_PUB_E ;
20442065 }
20452066
2067+ #if defined(WC_DH_NONBLOCK ) && defined(WOLFSSL_HAVE_SP_DH ) && \
2068+ defined(WOLFSSL_SP_NONBLOCK ) && defined(WOLFSSL_SP_SMALL ) && \
2069+ !defined(WOLFSSL_SP_FAST_MODEXP )
2070+ /* Non-blocking dispatch bypasses the mp_int dance entirely - the SP
2071+ * wrapper takes byte buffers and persists across yields. The constant-
2072+ * time fold-back (ct branch) is intentionally not applied here; nb
2073+ * callers should use the standard wc_DhAgree(). */
2074+ if (key -> nb != NULL && !ct ) {
2075+ #ifndef WOLFSSL_SP_NO_2048
2076+ if (mp_count_bits (& key -> p ) == 2048 ) {
2077+ return sp_DhExp_2048_nb (& key -> nb -> sp_ctx , otherPub , pubSz ,
2078+ priv , privSz , & key -> p , agree , agreeSz );
2079+ }
2080+ #endif
2081+ #ifndef WOLFSSL_SP_NO_3072
2082+ if (mp_count_bits (& key -> p ) == 3072 ) {
2083+ return sp_DhExp_3072_nb (& key -> nb -> sp_ctx , otherPub , pubSz ,
2084+ priv , privSz , & key -> p , agree , agreeSz );
2085+ }
2086+ #endif
2087+ #ifdef WOLFSSL_SP_4096
2088+ if (mp_count_bits (& key -> p ) == 4096 ) {
2089+ return sp_DhExp_4096_nb (& key -> nb -> sp_ctx , otherPub , pubSz ,
2090+ priv , privSz , & key -> p , agree , agreeSz );
2091+ }
2092+ #endif
2093+ /* size not nb-supported - fall through to blocking path */
2094+ }
2095+ #endif
2096+
20462097#if defined(WOLFSSL_SMALL_STACK ) && !defined(WOLFSSL_NO_MALLOC )
20472098 y = (mp_int * )XMALLOC (sizeof (mp_int ), key -> heap , DYNAMIC_TYPE_DH );
20482099 if (y == NULL )
@@ -2304,11 +2355,25 @@ int wc_DhAgree(DhKey* key, byte* agree, word32* agreeSz, const byte* priv,
23042355 ret = KcapiDh_SharedSecret (key , otherPub , pubSz , agree , agreeSz );
23052356#else
23062357#if defined(WOLFSSL_ASYNC_CRYPT ) && defined(WC_ASYNC_ENABLE_DH )
2358+ /* Async marker takes precedence: when wc_AsyncSimulate re-enters the
2359+ * compute path, wc_DhAgree_Async dispatches to the SP nonblock wrapper
2360+ * if key->nb is attached, and per-yield MP_WOULDBLOCK is translated to
2361+ * WC_PENDING_E by wc_AsyncSimulate so the TLS event loop drives it. */
23072362 if (key -> asyncDev .marker == WOLFSSL_ASYNC_MARKER_DH ) {
23082363 ret = wc_DhAgree_Async (key , agree , agreeSz , priv , privSz , otherPub ,
23092364 pubSz );
23102365 }
23112366 else
2367+ #endif
2368+ #ifdef WC_DH_NONBLOCK
2369+ /* Direct (non-async) nb dispatch - the caller (e.g. wolfcrypt test)
2370+ * drives the loop on MP_WOULDBLOCK directly. Reached when no async
2371+ * marker is set on the key. */
2372+ if (key -> nb != NULL ) {
2373+ ret = wc_DhAgree_Sync (key , agree , agreeSz , priv , privSz , otherPub ,
2374+ pubSz , 0 );
2375+ }
2376+ else
23122377#endif
23132378 {
23142379 ret = wc_DhAgree_Sync (key , agree , agreeSz , priv , privSz , otherPub ,
0 commit comments