Skip to content

Commit 2309476

Browse files
committed
Add RSA/DH SP non-blocking support for C/Small 2048/3072/4096
1 parent 5ffdb9f commit 2309476

13 files changed

Lines changed: 4253 additions & 21 deletions

File tree

.github/workflows/os-check.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,14 @@ jobs:
106106
'CPPFLAGS=-DNO_WOLFSSL_CLIENT',
107107
'CPPFLAGS=-DNO_WOLFSSL_SERVER',
108108
'--enable-lms=small,verify-only --enable-xmss=small,verify-only',
109-
'--enable-curve25519=nonblock --enable-ecc=nonblock --enable-sp=yes,nonblock CPPFLAGS="-DWOLFSSL_PUBLIC_MP -DWOLFSSL_DEBUG_NONBLOCK"',
109+
# Non-blocking ECC + Curve25519 + RSA + DH on the default SP word
110+
# size for the host (sp_c64.c on x86_64). RSA/DH non-block require
111+
# RSA_LOW_MEM (CRT path is not supported in non-block mode).
112+
'--enable-curve25519=nonblock --enable-ecc=nonblock --enable-rsa=nonblock --enable-dh=nonblock --enable-sp=yes,nonblock CPPFLAGS="-DWOLFSSL_PUBLIC_MP -DWOLFSSL_DEBUG_NONBLOCK -DRSA_LOW_MEM"',
113+
# Same configuration but force SP_WORD_SIZE=32 to exercise sp_c32.c
114+
# on a 64-bit host. The two builds together cover both generated
115+
# variants of mod_exp_<words>_nb / RSA / DH wrappers.
116+
'--enable-curve25519=nonblock --enable-ecc=nonblock --enable-rsa=nonblock --enable-dh=nonblock --enable-sp=yes,nonblock CPPFLAGS="-DWOLFSSL_PUBLIC_MP -DWOLFSSL_DEBUG_NONBLOCK -DRSA_LOW_MEM -DSP_WORD_SIZE=32"',
110117
'--enable-certreq --enable-certext --enable-certgen --disable-secure-renegotiation-info CPPFLAGS="-DNO_TLS"',
111118
]
112119
name: make check linux

configure.ac

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4880,6 +4880,15 @@ then
48804880
test -z "$enable_asynccrypt_sw" && enable_asynccrypt_sw=yes
48814881
fi
48824882

4883+
# Handle RSA/DH nonblock - the SP non-blocking dispatch wants the same
4884+
# WOLFSSL_ASYNC_CRYPT_SW shim that ECC/Curve25519 nonblock use so the
4885+
# TLS layer can manage per-SSL nb contexts and yield MP_WOULDBLOCK.
4886+
if test "$enable_rsa" = "nonblock" || test "$enable_dh" = "nonblock"
4887+
then
4888+
test -z "$enable_asynccrypt" && enable_asynccrypt=yes
4889+
test -z "$enable_asynccrypt_sw" && enable_asynccrypt_sw=yes
4890+
fi
4891+
48834892
if test "$ENABLED_CURVE25519" = "no" && test "$ENABLED_QUIC" = "yes" && test "$ENABLED_FIPS" = "no"
48844893
then
48854894
ENABLED_CURVE25519=yes
@@ -5396,14 +5405,25 @@ fi
53965405

53975406
# RSA
53985407
AC_ARG_ENABLE([rsa],
5399-
[AS_HELP_STRING([--enable-rsa],[Enable RSA (default: enabled)])],
5408+
[AS_HELP_STRING([--enable-rsa],[Enable RSA (default: enabled). Set to "nonblock" to enable non-blocking RSA via TFM fp_exptmod_nb or SP small mod_exp_nb])],
54005409
[ ENABLED_RSA=$enableval ],
54015410
[ ENABLED_RSA=yes ]
54025411
)
54035412

54045413
if test "$ENABLED_RSA" = "no"
54055414
then
54065415
AM_CFLAGS="$AM_CFLAGS -DNO_RSA"
5416+
elif test "$ENABLED_RSA" = "nonblock"
5417+
then
5418+
AM_CFLAGS="$AM_CFLAGS -DWC_RSA_NONBLOCK"
5419+
ENABLED_RSA=yes
5420+
ENABLED_CERTS=yes
5421+
# asynccrypt + asynccrypt-sw are auto-enabled earlier in this file when
5422+
# --enable-rsa=nonblock is detected, so the TLS layer can pick up the
5423+
# per-SSL nb context and yield MP_WOULDBLOCK. RSA_LOW_MEM is left as a
5424+
# user choice - the SP non-block backend's compile-time check in
5425+
# wolfssl/wolfcrypt/rsa.h enforces it for SP, while the TFM (fastmath)
5426+
# backend supports the CRT path without it.
54075427
else
54085428
# turn off RSA if leanpsk or leantls on
54095429
if test "$ENABLED_LEANPSK" = "yes" || test "$ENABLED_LEANTLS" = "yes"
@@ -5483,7 +5503,7 @@ fi
54835503

54845504
# DH
54855505
AC_ARG_ENABLE([dh],
5486-
[AS_HELP_STRING([--enable-dh],[Enable DH (default: enabled)])],
5506+
[AS_HELP_STRING([--enable-dh],[Enable DH (default: enabled). Set to "nonblock" to enable non-blocking DH key agreement via SP small mod_exp_nb])],
54875507
[ ENABLED_DH=$enableval ],
54885508
[ ENABLED_DH=yes ]
54895509
)
@@ -5496,6 +5516,11 @@ fi
54965516
if test "$ENABLED_DH" = "no"
54975517
then
54985518
AM_CFLAGS="$AM_CFLAGS -DNO_DH"
5519+
elif test "$ENABLED_DH" = "nonblock"
5520+
then
5521+
AM_CFLAGS="$AM_CFLAGS -DWC_DH_NONBLOCK"
5522+
ENABLED_DH=yes
5523+
# asynccrypt + asynccrypt-sw are auto-enabled earlier in this file.
54995524
else
55005525
# turn off DH if leanpsk or leantls on
55015526
if test "$ENABLED_LEANPSK" = "yes" || test "$ENABLED_LEANTLS" = "yes"

src/internal.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8300,6 +8300,15 @@ void FreeKey(WOLFSSL* ssl, int type, void** pKey)
83008300
switch (type) {
83018301
#ifndef NO_RSA
83028302
case DYNAMIC_TYPE_RSA:
8303+
#if defined(WC_RSA_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
8304+
defined(WC_ASYNC_ENABLE_RSA)
8305+
if (((RsaKey*)*pKey)->nb != NULL) {
8306+
XFREE(((RsaKey*)*pKey)->nb, ssl->heap,
8307+
DYNAMIC_TYPE_TMP_BUFFER);
8308+
((RsaKey*)*pKey)->nb = NULL;
8309+
}
8310+
#endif /* WC_RSA_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
8311+
WC_ASYNC_ENABLE_RSA */
83038312
wc_FreeRsaKey((RsaKey*)*pKey);
83048313
break;
83058314
#endif /* ! NO_RSA */
@@ -8355,6 +8364,15 @@ void FreeKey(WOLFSSL* ssl, int type, void** pKey)
83558364
#endif /* HAVE_DILITHIUM */
83568365
#ifndef NO_DH
83578366
case DYNAMIC_TYPE_DH:
8367+
#if defined(WC_DH_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
8368+
defined(WC_ASYNC_ENABLE_DH)
8369+
if (((DhKey*)*pKey)->nb != NULL) {
8370+
XFREE(((DhKey*)*pKey)->nb, ssl->heap,
8371+
DYNAMIC_TYPE_TMP_BUFFER);
8372+
((DhKey*)*pKey)->nb = NULL;
8373+
}
8374+
#endif /* WC_DH_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
8375+
WC_ASYNC_ENABLE_DH */
83588376
wc_FreeDhKey((DhKey*)*pKey);
83598377
break;
83608378
#endif /* !NO_DH */
@@ -8385,6 +8403,14 @@ int AllocKey(WOLFSSL* ssl, int type, void** pKey)
83858403
#if defined(WC_X25519_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW)
83868404
x25519_nb_ctx_t* x25519NbCtx;
83878405
#endif /* WC_X25519_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW */
8406+
#if !defined(NO_RSA) && defined(WC_RSA_NONBLOCK) && \
8407+
defined(WOLFSSL_ASYNC_CRYPT_SW) && defined(WC_ASYNC_ENABLE_RSA)
8408+
RsaNb* rsaNb;
8409+
#endif
8410+
#if !defined(NO_DH) && defined(WC_DH_NONBLOCK) && \
8411+
defined(WOLFSSL_ASYNC_CRYPT_SW) && defined(WC_ASYNC_ENABLE_DH)
8412+
DhNb* dhNb;
8413+
#endif
83888414

83898415
if (ssl == NULL || pKey == NULL) {
83908416
return BAD_FUNC_ARG;
@@ -8464,6 +8490,26 @@ int AllocKey(WOLFSSL* ssl, int type, void** pKey)
84648490
#ifndef NO_RSA
84658491
case DYNAMIC_TYPE_RSA:
84668492
ret = wc_InitRsaKey_ex((RsaKey*)*pKey, ssl->heap, ssl->devId);
8493+
#if defined(WC_RSA_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
8494+
defined(WC_ASYNC_ENABLE_RSA)
8495+
/* Only set non-blocking context when async device is active. With
8496+
* INVALID_DEVID there is no async loop to retry on MP_WOULDBLOCK, so
8497+
* skip non-blocking setup and use blocking mode instead. */
8498+
if (ret == 0 && ssl->devId != INVALID_DEVID) {
8499+
rsaNb = (RsaNb*)XMALLOC(sizeof(RsaNb), ssl->heap,
8500+
DYNAMIC_TYPE_TMP_BUFFER);
8501+
if (rsaNb == NULL) {
8502+
ret = MEMORY_E;
8503+
}
8504+
else {
8505+
ret = wc_RsaSetNonBlock((RsaKey*)*pKey, rsaNb);
8506+
if (ret != 0) {
8507+
XFREE(rsaNb, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
8508+
}
8509+
}
8510+
}
8511+
#endif /* WC_RSA_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
8512+
WC_ASYNC_ENABLE_RSA */
84678513
break;
84688514
#endif /* ! NO_RSA */
84698515
#ifdef HAVE_ECC
@@ -8551,6 +8597,26 @@ int AllocKey(WOLFSSL* ssl, int type, void** pKey)
85518597
#ifndef NO_DH
85528598
case DYNAMIC_TYPE_DH:
85538599
ret = wc_InitDhKey_ex((DhKey*)*pKey, ssl->heap, ssl->devId);
8600+
#if defined(WC_DH_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
8601+
defined(WC_ASYNC_ENABLE_DH)
8602+
/* Only set non-blocking context when async device is active. With
8603+
* INVALID_DEVID there is no async loop to retry on MP_WOULDBLOCK, so
8604+
* skip non-blocking setup and use blocking mode instead. */
8605+
if (ret == 0 && ssl->devId != INVALID_DEVID) {
8606+
dhNb = (DhNb*)XMALLOC(sizeof(DhNb), ssl->heap,
8607+
DYNAMIC_TYPE_TMP_BUFFER);
8608+
if (dhNb == NULL) {
8609+
ret = MEMORY_E;
8610+
}
8611+
else {
8612+
ret = wc_DhSetNonBlock((DhKey*)*pKey, dhNb);
8613+
if (ret != 0) {
8614+
XFREE(dhNb, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
8615+
}
8616+
}
8617+
}
8618+
#endif /* WC_DH_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
8619+
WC_ASYNC_ENABLE_DH */
85548620
break;
85558621
#endif /* !NO_DH */
85568622
default:

src/tls.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8010,6 +8010,26 @@ static int TLSX_KeyShare_GenDhKey(WOLFSSL *ssl, KeyShareEntry* kse)
80108010
ret = wc_DhSetNamedKey(dhKey, kse->group);
80118011
#endif
80128012
}
8013+
#if defined(WC_DH_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
8014+
defined(WC_ASYNC_ENABLE_DH)
8015+
/* Only set non-blocking context when async device is active. With
8016+
* INVALID_DEVID there is no async loop to retry on MP_WOULDBLOCK, so
8017+
* skip non-blocking setup and use blocking mode instead. */
8018+
if (ret == 0 && ssl->devId != INVALID_DEVID) {
8019+
DhNb* dhNb = (DhNb*)XMALLOC(sizeof(DhNb), ssl->heap,
8020+
DYNAMIC_TYPE_TMP_BUFFER);
8021+
if (dhNb == NULL) {
8022+
ret = MEMORY_E;
8023+
}
8024+
else {
8025+
ret = wc_DhSetNonBlock((DhKey*)kse->key, dhNb);
8026+
if (ret != 0) {
8027+
XFREE(dhNb, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
8028+
}
8029+
}
8030+
}
8031+
#endif /* WC_DH_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
8032+
WC_ASYNC_ENABLE_DH */
80138033
}
80148034

80158035
/* Allocate space for the private and public key */
@@ -9297,6 +9317,26 @@ static int TLSX_KeyShare_ProcessDh(WOLFSSL* ssl, KeyShareEntry* keyShareEntry)
92979317
ret = wc_DhSetNamedKey(dhKey, keyShareEntry->group);
92989318
#endif
92999319
}
9320+
#if defined(WC_DH_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
9321+
defined(WC_ASYNC_ENABLE_DH)
9322+
/* Only set non-blocking context when async device is active. With
9323+
* INVALID_DEVID there is no async loop to retry on MP_WOULDBLOCK, so
9324+
* skip non-blocking setup and use blocking mode instead. */
9325+
if (ret == 0 && ssl->devId != INVALID_DEVID) {
9326+
DhNb* dhNb = (DhNb*)XMALLOC(sizeof(DhNb), ssl->heap,
9327+
DYNAMIC_TYPE_TMP_BUFFER);
9328+
if (dhNb == NULL) {
9329+
ret = MEMORY_E;
9330+
}
9331+
else {
9332+
ret = wc_DhSetNonBlock((DhKey*)keyShareEntry->key, dhNb);
9333+
if (ret != 0) {
9334+
XFREE(dhNb, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
9335+
}
9336+
}
9337+
}
9338+
#endif /* WC_DH_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
9339+
WC_ASYNC_ENABLE_DH */
93009340
}
93019341

93029342
if (ret == 0

wolfcrypt/src/dh.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

9841005
int 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,13 +2355,20 @@ 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
23122367
#endif
23132368
{
2369+
/* wc_DhAgree_Sync handles key->nb internally; no separate dispatch
2370+
* needed here. wc_DhAgree_ct (constant-time fold-back) bypasses
2371+
* this function entirely so passing ct=0 is correct. */
23142372
ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub,
23152373
pubSz, 0);
23162374
}

0 commit comments

Comments
 (0)