diff --git a/doc/dox_comments/header_files/rsa.h b/doc/dox_comments/header_files/rsa.h index 878dea9be..0d1385308 100644 --- a/doc/dox_comments/header_files/rsa.h +++ b/doc/dox_comments/header_files/rsa.h @@ -791,3 +791,81 @@ WOLFSSL_API int wc_RsaKeyToPublicDer(RsaKey*, byte* output, word32 inLen); \sa none */ WOLFSSL_API int wc_MakeRsaKey(RsaKey* key, int size, long e, WC_RNG* rng); + +/*! + \ingroup RSA + + \brief This function sets the non-blocking RSA context. When a RsaNb context + is set it enables fast math based non-blocking exptmod, which splits the RSA + function into many smaller operations. + Enabled when WC_RSA_NONBLOCK is defined. + + \return 0 Success + \return BAD_FUNC_ARG Returned if key or nb is null. + + \param key The RSA key structure + \param nb The RSA non-blocking structure for this RSA key to use. + + _Example_ + \code + int ret, count = 0; + RsaKey key; + RsaNb nb; + + wc_RsaInitKey(&key, NULL); + + // Enable non-blocking RSA mode - provide context + ret = wc_RsaSetNonBlock(key, &nb); + if (ret != 0) + return ret; + + do { + ret = wc_RsaSSL_Sign(in, inLen, out, outSz, key, rng); + count++; // track number of would blocks + if (ret == FP_WOULDBLOCK) { + // do "other" work here + } + } while (ret == FP_WOULDBLOCK); + if (ret < 0) { + return ret; + } + + printf("RSA non-block sign: size %d, %d times\n", ret, count); + \endcode + + \sa wc_RsaSetNonBlockTime +*/ +WOLFSSL_API int wc_RsaSetNonBlock(RsaKey* key, RsaNb* nb); + +/*! + \ingroup RSA + + \brief This function configures the maximum amount of blocking time in + microseconds. It uses a pre-computed table (see tfm.c exptModNbInst) along + with the CPU speed in megahertz to determine if the next operation can be + completed within the maximum blocking time provided. + Enabled when WC_RSA_NONBLOCK_TIME is defined. + + \return 0 Success + \return BAD_FUNC_ARG Returned if key is null or wc_RsaSetNonBlock was not + previously called and key->nb is null. + + \param key The RSA key structure. + \param maxBlockUs Maximum time to block microseconds. + \param cpuMHz CPU speed in megahertz. + + _Example_ + \code + RsaKey key; + RsaNb nb; + + wc_RsaInitKey(&key, NULL); + wc_RsaSetNonBlock(key, &nb); + wc_RsaSetNonBlockTime(&key, 4000, 160); // Block Max = 4 ms, CPU = 160MHz + + \endcode + + \sa wc_RsaSetNonBlock +*/ +WOLFSSL_API int wc_RsaSetNonBlockTime(RsaKey* key, word32 maxBlockUs, + word32 cpuMHz); diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index ddfd665ad..da04481d3 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -55,7 +55,9 @@ Possible RSA enable options: * WOLFSSL_KEY_GEN: Allows Private Key Generation default: off * RSA_LOW_MEM: NON CRT Private Operations, less memory default: off * WC_NO_RSA_OAEP: Disables RSA OAEP padding default: on (not defined) - + * WC_RSA_NONBLOCK: Enables support for RSA non-blocking default: off + * WC_RSA_NONBLOCK_TIME:Enables support for time based blocking default: off + * time calculation. */ /* @@ -2814,7 +2816,7 @@ int wc_RsaEncryptSize(RsaKey* key) return BAD_FUNC_ARG; } - ret = mp_unsigned_bin_size(&key->n); + ret = mp_unsigned_bin_size(&key->n); #ifdef WOLF_CRYPTO_DEV if (ret == 0 && key->devId != INVALID_DEVID) { @@ -3400,6 +3402,19 @@ int wc_RsaSetNonBlock(RsaKey* key, RsaNb* nb) return 0; } +#ifdef WC_RSA_NONBLOCK_TIME +int wc_RsaSetNonBlockTime(RsaKey* key, word32 maxBlockUs, word32 cpuMHz) +{ + if (key == NULL || key->nb == NULL) { + return BAD_FUNC_ARG; + } + + /* calculate maximum number of instructions to block */ + key->nb->exptmod.maxBlockInst = cpuMHz * maxBlockUs; + + return 0; +} +#endif /* WC_RSA_NONBLOCK_TIME */ #endif /* WC_RSA_NONBLOCK */ #endif /* NO_RSA */ diff --git a/wolfcrypt/src/tfm.c b/wolfcrypt/src/tfm.c index a5af9d63c..069689e12 100644 --- a/wolfcrypt/src/tfm.c +++ b/wolfcrypt/src/tfm.c @@ -1196,15 +1196,80 @@ int fp_addmod(fp_int *a, fp_int *b, fp_int *c, fp_int *d) #ifdef WC_RSA_NONBLOCK +#ifdef WC_RSA_NONBLOCK_TIME + /* User can override the check-time at build-time using the + * FP_EXPTMOD_NB_CHECKTIME macro to define your own function */ + #ifndef FP_EXPTMOD_NB_CHECKTIME + /* instruction count for each type of operation */ + /* array lookup is using TFM_EXPTMOD_NB_* states */ + static const word32 exptModNbInst[TFM_EXPTMOD_NB_COUNT] = { + #ifdef TFM_PPC32 + #ifdef _DEBUG + 11098, 8701, 3971, 178394, 858093, 1040, 822, 178056, 181574, 90883, 184339, 236813 + #else + 7050, 2554, 3187, 43178, 200422, 384, 275, 43024, 43550, 30450, 46270, 61376 + #endif + #elif defined(TFM_X86_64) + #ifdef _DEBUG + 954, 2377, 858, 19027, 90840, 287, 407, 20140, 7874, 11385, 8005, 6151 + #else + 765, 1007, 771, 5216, 34993, 248, 193, 4975, 4201, 3947, 4275, 3811 + #endif + #else /* software only fast math */ + #ifdef _DEBUG + 798, 2245, 802, 16657, 66920, 352, 186, 16997, 16145, 12789, 16742, 15006 + #else + 775, 1084, 783, 4692, 37510, 207, 183, 4374, 4392, 3097, 4442, 4079 + #endif + #endif + }; + + static int fp_exptmod_nb_checktime(exptModNb_t* nb) + { + word32 totalInst; + + /* if no max time has been set then stop (do not block) */ + if (nb->maxBlockInst == 0 || nb->state >= TFM_EXPTMOD_NB_COUNT) { + return TFM_EXPTMOD_NB_STOP; + } + + /* if instruction table not set then use maxBlockInst as simple counter */ + if (exptModNbInst[nb->state] == 0) { + if (++nb->totalInst < nb->maxBlockInst) + return TFM_EXPTMOD_NB_CONTINUE; + + nb->totalInst = 0; /* reset counter */ + return TFM_EXPTMOD_NB_STOP; + } + + /* get total instruction count including next operation */ + totalInst = nb->totalInst + exptModNbInst[nb->state]; + /* if the next operation can completed within the maximum then continue */ + if (totalInst <= nb->maxBlockInst) { + return TFM_EXPTMOD_NB_CONTINUE; + } + + return TFM_EXPTMOD_NB_STOP; + } + #define FP_EXPTMOD_NB_CHECKTIME(nb) fp_exptmod_nb_checktime((nb)) + #endif /* !FP_EXPTMOD_NB_CHECKTIME */ +#endif /* WC_RSA_NONBLOCK_TIME */ + /* non-blocking version of timing resistant fp_exptmod function */ /* supports cache resistance */ int fp_exptmod_nb(exptModNb_t* nb, fp_int* G, fp_int* X, fp_int* P, fp_int* Y) { - int err; + int err, ret = FP_WOULDBLOCK; if (nb == NULL) return FP_VAL; +#ifdef WC_RSA_NONBLOCK_TIME + nb->totalInst = 0; + do { + nb->totalInst += exptModNbInst[nb->state]; +#endif + switch (nb->state) { case TFM_EXPTMOD_NB_INIT: /* now setup montgomery */ @@ -1224,7 +1289,7 @@ int fp_exptmod_nb(exptModNb_t* nb, fp_int* G, fp_int* X, fp_int* P, fp_int* Y) case TFM_EXPTMOD_NB_MONT: /* mod m -> R[0] */ - fp_montgomery_calc_normalization (&nb->R[0], P); + fp_montgomery_calc_normalization(&nb->R[0], P); nb->state = TFM_EXPTMOD_NB_MONT_RED; break; @@ -1338,10 +1403,17 @@ int fp_exptmod_nb(exptModNb_t* nb, fp_int* G, fp_int* X, fp_int* P, fp_int* Y) fp_copy(&nb->R[0], Y); nb->state = TFM_EXPTMOD_NB_INIT; - return FP_OKAY; + ret = FP_OKAY; + break; } /* switch */ - return FP_WOULDBLOCK; +#ifdef WC_RSA_NONBLOCK_TIME + /* determine if maximum blocking time has been reached */ + } while (ret == FP_WOULDBLOCK && + FP_EXPTMOD_NB_CHECKTIME(nb) == TFM_EXPTMOD_NB_CONTINUE); +#endif + + return ret; } #endif /* WC_RSA_NONBLOCK */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 89437b370..a79e2cc13 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -9393,6 +9393,13 @@ static int rsa_nb_test(RsaKey* key, const byte* in, word32 inLen, byte* out, if (ret != 0) return ret; +#ifdef WC_RSA_NONBLOCK_TIME + /* Enable time based RSA blocking. 8 microseconds max (3.1GHz) */ + ret = wc_RsaSetNonBlockTime(key, 8, 3100); + if (ret != 0) + return ret; +#endif + count = 0; do { ret = wc_RsaSSL_Sign(in, inLen, out, outSz, key, rng); diff --git a/wolfssl/wolfcrypt/rsa.h b/wolfssl/wolfcrypt/rsa.h index 8c4f36f56..c68008bfb 100644 --- a/wolfssl/wolfcrypt/rsa.h +++ b/wolfssl/wolfcrypt/rsa.h @@ -43,6 +43,9 @@ #ifndef USE_FAST_MATH #error RSA non-blocking mode only supported using fast math #endif + #ifndef TFM_TIMING_RESISTANT + #error RSA non-blocking mode only supported with timing resistance enabled + #endif /* RSA bounds check is not supported with RSA non-blocking mode */ #undef NO_RSA_BOUNDS_CHECK @@ -268,6 +271,10 @@ WOLFSSL_API int wc_RsaPublicKeyDecodeRaw(const byte* n, word32 nSz, #endif #ifdef WC_RSA_NONBLOCK WOLFSSL_API int wc_RsaSetNonBlock(RsaKey* key, RsaNb* nb); + #ifdef WC_RSA_NONBLOCK_TIME + WOLFSSL_API int wc_RsaSetNonBlockTime(RsaKey* key, word32 maxBlockUs, + word32 cpuMHz); + #endif #endif /* diff --git a/wolfssl/wolfcrypt/tfm.h b/wolfssl/wolfcrypt/tfm.h index ab2b7c86c..16c920d3e 100644 --- a/wolfssl/wolfcrypt/tfm.h +++ b/wolfssl/wolfcrypt/tfm.h @@ -554,6 +554,7 @@ enum tfmExptModNbState { TFM_EXPTMOD_NB_SQR, TFM_EXPTMOD_NB_SQR_RED, TFM_EXPTMOD_NB_RED, + TFM_EXPTMOD_NB_COUNT /* last item for total state count only */ }; typedef struct { @@ -562,13 +563,25 @@ typedef struct { #else fp_int R[2]; #endif - fp_digit buf, mp; + fp_digit buf; + fp_digit mp; int bitcnt; int digidx; int y; int state; /* tfmExptModNbState */ +#ifdef WC_RSA_NONBLOCK_TIME + word32 maxBlockInst; /* maximum instructions to block */ + word32 totalInst; /* tracks total instructions */ +#endif } exptModNb_t; +#ifdef WC_RSA_NONBLOCK_TIME +enum { + TFM_EXPTMOD_NB_STOP = 0, /* stop and return FP_WOULDBLOCK */ + TFM_EXPTMOD_NB_CONTINUE = 1, /* keep blocking */ +}; +#endif + /* non-blocking version of timing resistant fp_exptmod function */ /* supports cache resistance */ int fp_exptmod_nb(exptModNb_t* nb, fp_int* G, fp_int* X, fp_int* P, fp_int* Y);