diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index 2dcbdfba8d2..0c4843561a7 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -542,6 +542,14 @@ "comment": "RSA verify-only with ECC cofactor ECDH (HAVE_ECC_CDH, set by --enable-ecccustcurves=all), whose wc_ecc_shared_secret_gen_sync() calls mp_mul_d. SAKKE stays off, so the HAVE_ECC_CDH term in settings.h is the only reason sp_mul_d is built. This gives link-time coverage of that term only: the mp_mul_d call runs only when cofactor != 1, and no curve at or above ECC_MIN_KEY_SZ has another cofactor.", "configure": ["--enable-cryptonly", "--enable-rsavfy", "--enable-ecccustcurves=all", "--disable-examples"]}, +{"name": "cryptonly-rsapub-no-dh-public-mp", "minutes": 1.0, + "comment": "RSA public only with DH off, so sp_exptmod_ex hands every modulus to sp_exptmod_nct and the constant-time generic exponentiation is not compiled. WOLFSSL_PUBLIC_MP lets wolfcrypt test run mp_test, whose even modulus checks confirm sp_exptmod_nct returns MP_VAL in this shape instead of a wrong Montgomery result.", + "configure": ["--enable-cryptonly", "--enable-rsapub", "--disable-dh", + "--disable-examples", "CPPFLAGS=-DWOLFSSL_PUBLIC_MP"]}, +{"name": "cryptonly-rsapub-public-mp", "minutes": 1.0, + "comment": "RSA public only with DH at its default (on), so the generic exponentiation is compiled and sp_exptmod_nct must use it for an even modulus rather than Montgomery. WOLFSSL_PUBLIC_MP lets wolfcrypt test run mp_test's even modulus checks in this shape.", + "configure": ["--enable-cryptonly", "--enable-rsapub", + "--disable-examples", "CPPFLAGS=-DWOLFSSL_PUBLIC_MP"]}, {"name": "pkcs12-pbkdf-mp-api", "minutes": 1.0, "comment": "WC_PKCS12_PBKDF_USING_MP_API selects a second wc_PKCS12_PBKDF_ex implementation that no configure option reaches.", "configure": ["--enable-pkcs12", "--enable-des3", "--enable-opensslextra", diff --git a/wolfcrypt/src/sp_int.c b/wolfcrypt/src/sp_int.c index 24d65221cad..5623b72d9bf 100644 --- a/wolfcrypt/src/sp_int.c +++ b/wolfcrypt/src/sp_int.c @@ -14199,7 +14199,8 @@ static int _sp_exptmod_base_2(const sp_int* e, int digits, const sp_int* m, * * @return MP_OKAY on success. * @return MP_VAL when b, e, m or r is NULL, digits is negative, or m <= 0 or - * e is negative. + * e is negative; or m is even in an RSA verify/public only build + * without DH or OPENSSL_ALL. * @return MP_MEM when dynamic memory allocation fails. */ int sp_exptmod_ex(const sp_int* b, const sp_int* e, int digits, const sp_int* m, @@ -14379,7 +14380,9 @@ int sp_exptmod_ex(const sp_int* b, const sp_int* e, int digits, const sp_int* m, * @param [out] r SP integer to hold result. * * @return MP_OKAY on success. - * @return MP_VAL when b, e, m or r is NULL; or m <= 0 or e is negative. + * @return MP_VAL when b, e, m or r is NULL; or m <= 0 or e is negative; or + * m is even in an RSA verify/public only build without DH or + * OPENSSL_ALL. * @return MP_MEM when dynamic memory allocation fails. */ int sp_exptmod(const sp_int* b, const sp_int* e, const sp_int* m, sp_int* r) @@ -14839,7 +14842,9 @@ static int _sp_exptmod_nct(const sp_int* b, const sp_int* e, const sp_int* m, * @param [out] r SP integer to hold result. * * @return MP_OKAY on success. - * @return MP_VAL when b, e, m or r is NULL; or m <= 0 or e is negative. + * @return MP_VAL when b, e, m or r is NULL; or m <= 0 or e is negative; or + * m is even in an RSA verify/public only build without DH or + * OPENSSL_ALL. * @return MP_MEM when dynamic memory allocation fails. */ int sp_exptmod_nct(const sp_int* b, const sp_int* e, const sp_int* m, sp_int* r) @@ -14886,11 +14891,18 @@ int sp_exptmod_nct(const sp_int* b, const sp_int* e, const sp_int* m, sp_int* r) else if (m->used * 2 >= r->size) { err = MP_VAL; } -#if !defined(WOLFSSL_RSA_VERIFY_ONLY) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) else if (mp_iseven(m)) { +#if (defined(WOLFSSL_SP_MATH_ALL) && !defined(WOLFSSL_RSA_VERIFY_ONLY) && \ + !defined(WOLFSSL_RSA_PUBLIC_ONLY)) || !defined(NO_DH) || \ + defined(OPENSSL_ALL) err = _sp_exptmod_ex(b, e, (int)(e->used * SP_WORD_SIZE), m, r); - } +#else + /* Montgomery form requires an odd modulus and the generic + * implementation is not compiled in (RSA verify/public only without + * DH). */ + err = MP_VAL; #endif + } else { err = _sp_exptmod_nct(b, e, m, r); } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 9fabf2da026..6278b71b14c 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -95254,12 +95254,28 @@ static wc_test_ret_t mp_test_exptmod(mp_int* b, mp_int* e, mp_int* m, mp_int* r, /* Even, multi-word modulus uses the generic exponentiation path for * both mp_exptmod_ex and mp_exptmod_nct. Verify b^6 mod m against - * repeated modular multiplication. */ + * repeated modular multiplication. RSA public only builds without DH + * do not compile the generic path, and mp_exptmod_ex hands every modulus + * to mp_exptmod_nct, so both must reject the even modulus instead of + * returning a wrong Montgomery result. */ if ((ret = mp_set(m, 1)) != MP_OKAY || (ret = mp_mul_2d(m, 521, m)) != MP_OKAY || /* m = 2^521 (even) */ (ret = mp_set(b, 7)) != MP_OKAY || - (ret = mp_set(e, 6)) != MP_OKAY || - (ret = mp_set(t, 1)) != MP_OKAY) { + (ret = mp_set(e, 6)) != MP_OKAY) { + return WC_TEST_RET_ENC_EC(ret); + } +#if (defined(WOLFSSL_RSA_VERIFY_ONLY) || defined(WOLFSSL_RSA_PUBLIC_ONLY)) && \ + defined(NO_DH) && !defined(OPENSSL_ALL) + ret = mp_exptmod_ex(b, e, (int)e->used, m, r); + if (ret != WC_NO_ERR_TRACE(MP_VAL)) { + return WC_TEST_RET_ENC_EC(ret); + } + ret = mp_exptmod_nct(b, e, m, r); + if (ret != WC_NO_ERR_TRACE(MP_VAL)) { + return WC_TEST_RET_ENC_EC(ret); + } +#else + if ((ret = mp_set(t, 1)) != MP_OKAY) { return WC_TEST_RET_ENC_EC(ret); } for (k = 0; k < 6; k++) { @@ -95279,6 +95295,7 @@ static wc_test_ret_t mp_test_exptmod(mp_int* b, mp_int* e, mp_int* m, mp_int* r, if (mp_cmp(r, t) != MP_EQ) { return WC_TEST_RET_ENC_NC; } +#endif #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)