From d0ecb8cc3d8b0920e9f21f6c7026bc262f92805c Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Wed, 15 Jan 2025 17:49:12 -0500 Subject: [PATCH] Modify Montgomery exponentiation to return results in Montgomery form In RSA, keep the values in Montgomery form using the same flow as described as "Smooth-CRT RSA" in https://eprint.iacr.org/2007/039.pdf DSA and ElGamal just immediately convert the result to standard form; the performance of these doesn't matter much at all anymore. --- doc/deprecated.rst | 5 ++ src/lib/math/numbertheory/monty.cpp | 28 +++++++++-- src/lib/math/numbertheory/monty.h | 9 ++++ src/lib/math/numbertheory/monty_exp.cpp | 66 +++++++++++++------------ src/lib/math/numbertheory/monty_exp.h | 45 ++++++++++------- src/lib/math/numbertheory/numthry.cpp | 10 ++-- src/lib/math/numbertheory/primality.cpp | 2 +- src/lib/pubkey/dl_group/dl_group.cpp | 12 +++-- src/lib/pubkey/elgamal/elgamal.cpp | 2 +- src/lib/pubkey/rsa/rsa.cpp | 55 +++++++++++---------- 10 files changed, 141 insertions(+), 93 deletions(-) diff --git a/doc/deprecated.rst b/doc/deprecated.rst index 787b7f45872..dc165fe4e84 100644 --- a/doc/deprecated.rst +++ b/doc/deprecated.rst @@ -187,6 +187,11 @@ release, or where a backwards incompatible change is expected. - All support for loading, generating or using RSA keys with a public exponent larger than 2**64-1 +- Currently RSA_PrivateKey will allow generating any key of bitlength + greater than or equal to 1024 bits. In a future major release the + allowed bitlengths of new RSA keys will be restricted to 2048 bits + or higher, and the bitlength must be a multiple of 1024 bits. + - Currently some public key padding mechanisms can be used with several different names. This is deprecated. "EMSA_PKCS1", "EMSA-PKCS1-v1_5", "EMSA3": Use "PKCS1v15" diff --git a/src/lib/math/numbertheory/monty.cpp b/src/lib/math/numbertheory/monty.cpp index 09b57de928b..cb2489846dd 100644 --- a/src/lib/math/numbertheory/monty.cpp +++ b/src/lib/math/numbertheory/monty.cpp @@ -260,6 +260,18 @@ void Montgomery_Params::square_this(BigInt& x, secure_vector& ws) const { copy_mem(x.mutable_data(), z_data, output_size); } +Montgomery_Int Montgomery_Int::one(const std::shared_ptr& params) { + return Montgomery_Int(params, params->R1(), false); +} + +Montgomery_Int Montgomery_Int::from_wide_int(const std::shared_ptr& params, const BigInt& x) { + //BOTAN_ARG_CHECK(x < params->p() * params->p(), "Input too large"); + + secure_vector ws; + auto redc_x = params->mul(params->redc(x, ws), params->R3(), ws); + return Montgomery_Int(params, redc_x, false); +} + Montgomery_Int::Montgomery_Int(const std::shared_ptr& params, const BigInt& v, bool redc_needed) : @@ -301,11 +313,7 @@ Montgomery_Int::Montgomery_Int(std::shared_ptr params, void Montgomery_Int::fix_size() { const size_t p_words = m_params->p_words(); - - if(m_v.sig_words() > p_words) { - throw Internal_Error("Montgomery_Int::fix_size v too large"); - } - + BOTAN_DEBUG_ASSERT(m_v.sig_words() <= p_words); m_v.grow_to(p_words); } @@ -335,6 +343,7 @@ BigInt Montgomery_Int::value() const { } Montgomery_Int Montgomery_Int::operator+(const Montgomery_Int& other) const { + BOTAN_STATE_CHECK(other.m_params == m_params); secure_vector ws; BigInt z = m_v; z.mod_add(other.m_v, m_params->p(), ws); @@ -342,6 +351,7 @@ Montgomery_Int Montgomery_Int::operator+(const Montgomery_Int& other) const { } Montgomery_Int Montgomery_Int::operator-(const Montgomery_Int& other) const { + BOTAN_STATE_CHECK(other.m_params == m_params); secure_vector ws; BigInt z = m_v; z.mod_sub(other.m_v, m_params->p(), ws); @@ -349,35 +359,42 @@ Montgomery_Int Montgomery_Int::operator-(const Montgomery_Int& other) const { } Montgomery_Int& Montgomery_Int::operator+=(const Montgomery_Int& other) { + BOTAN_STATE_CHECK(other.m_params == m_params); secure_vector ws; return this->add(other, ws); } Montgomery_Int& Montgomery_Int::add(const Montgomery_Int& other, secure_vector& ws) { + BOTAN_STATE_CHECK(other.m_params == m_params); m_v.mod_add(other.m_v, m_params->p(), ws); return (*this); } Montgomery_Int& Montgomery_Int::operator-=(const Montgomery_Int& other) { + BOTAN_STATE_CHECK(other.m_params == m_params); secure_vector ws; return this->sub(other, ws); } Montgomery_Int& Montgomery_Int::sub(const Montgomery_Int& other, secure_vector& ws) { + BOTAN_STATE_CHECK(other.m_params == m_params); m_v.mod_sub(other.m_v, m_params->p(), ws); return (*this); } Montgomery_Int Montgomery_Int::operator*(const Montgomery_Int& other) const { + BOTAN_STATE_CHECK(other.m_params == m_params); secure_vector ws; return Montgomery_Int(m_params, m_params->mul(m_v, other.m_v, ws), false); } Montgomery_Int Montgomery_Int::mul(const Montgomery_Int& other, secure_vector& ws) const { + BOTAN_STATE_CHECK(other.m_params == m_params); return Montgomery_Int(m_params, m_params->mul(m_v, other.m_v, ws), false); } Montgomery_Int& Montgomery_Int::mul_by(const Montgomery_Int& other, secure_vector& ws) { + BOTAN_STATE_CHECK(other.m_params == m_params); m_params->mul_by(m_v, other.m_v, ws); return (*this); } @@ -388,6 +405,7 @@ Montgomery_Int& Montgomery_Int::mul_by(const secure_vector& other, secure_ } Montgomery_Int& Montgomery_Int::operator*=(const Montgomery_Int& other) { + BOTAN_STATE_CHECK(other.m_params == m_params); secure_vector ws; return mul_by(other, ws); } diff --git a/src/lib/math/numbertheory/monty.h b/src/lib/math/numbertheory/monty.h index 5090b210f77..7e23305c9d3 100644 --- a/src/lib/math/numbertheory/monty.h +++ b/src/lib/math/numbertheory/monty.h @@ -48,6 +48,13 @@ class BOTAN_TEST_API Montgomery_Int final { size_t len, bool redc_needed = true); + static Montgomery_Int one(const std::shared_ptr& params); + + /** + * Wide reduction - input can be at most 2*bytes long + */ + static Montgomery_Int from_wide_int(const std::shared_ptr& params, const BigInt& x); + bool operator==(const Montgomery_Int& other) const; bool operator!=(const Montgomery_Int& other) const { return (m_v != other.m_v); } @@ -118,6 +125,8 @@ class BOTAN_TEST_API Montgomery_Int final { void _const_time_unpoison() const { CT::unpoison(m_v); } + const std::shared_ptr& _params() const { return m_params; } + private: std::shared_ptr m_params; BigInt m_v; diff --git a/src/lib/math/numbertheory/monty_exp.cpp b/src/lib/math/numbertheory/monty_exp.cpp index 28b09d162f8..467d0bbe8ee 100644 --- a/src/lib/math/numbertheory/monty_exp.cpp +++ b/src/lib/math/numbertheory/monty_exp.cpp @@ -1,6 +1,6 @@ /* * Montgomery Exponentiation -* (C) 1999-2010,2012,2018 Jack Lloyd +* (C) 1999-2010,2012,2018,2025 Jack Lloyd * 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) @@ -18,14 +18,11 @@ namespace Botan { class Montgomery_Exponentation_State final { public: - Montgomery_Exponentation_State(const std::shared_ptr& params, - const BigInt& g, - size_t window_bits, - bool const_time); + Montgomery_Exponentation_State(const Montgomery_Int& g, size_t window_bits, bool const_time); - BigInt exponentiation(const BigInt& k, size_t max_k_bits) const; + Montgomery_Int exponentiation(const BigInt& k, size_t max_k_bits) const; - BigInt exponentiation_vartime(const BigInt& k) const; + Montgomery_Int exponentiation_vartime(const BigInt& k) const; private: std::shared_ptr m_params; @@ -33,13 +30,10 @@ class Montgomery_Exponentation_State final { size_t m_window_bits; }; -Montgomery_Exponentation_State::Montgomery_Exponentation_State(const std::shared_ptr& params, - const BigInt& g, +Montgomery_Exponentation_State::Montgomery_Exponentation_State(const Montgomery_Int& g, size_t window_bits, bool const_time) : - m_params(params), m_window_bits(window_bits == 0 ? 4 : window_bits) { - BOTAN_ARG_CHECK(g < m_params->p(), "Montgomery base too big"); - + m_params(g._params()), m_window_bits(window_bits == 0 ? 4 : window_bits) { if(m_window_bits < 1 || m_window_bits > 12) { // really even 8 is too large ... throw Invalid_Argument("Invalid window bits for Montgomery exponentiation"); } @@ -48,17 +42,17 @@ Montgomery_Exponentation_State::Montgomery_Exponentation_State(const std::shared m_g.reserve(window_size); - m_g.push_back(Montgomery_Int(m_params, m_params->R1(), false)); + m_g.push_back(Montgomery_Int::one(m_params)); - m_g.push_back(Montgomery_Int(m_params, g)); + m_g.push_back(g); for(size_t i = 2; i != window_size; ++i) { m_g.push_back(m_g[1] * m_g[i - 1]); } // Resize each element to exactly p words - for(size_t i = 0; i != window_size; ++i) { - m_g[i].fix_size(); + for(auto& x : m_g) { + x.fix_size(); } if(const_time) { @@ -93,14 +87,14 @@ void const_time_lookup(secure_vector& output, const std::vector e_bits(m_params->p_words()); @@ -116,16 +110,16 @@ BigInt Montgomery_Exponentation_State::exponentiation(const BigInt& scalar, size } CT::unpoison(x); - return x.value(); + return x; } -BigInt Montgomery_Exponentation_State::exponentiation_vartime(const BigInt& scalar) const { +Montgomery_Int Montgomery_Exponentation_State::exponentiation_vartime(const BigInt& scalar) const { const size_t exp_nibbles = (scalar.bits() + m_window_bits - 1) / m_window_bits; secure_vector ws; if(exp_nibbles == 0) { - return BigInt::one(); + return Montgomery_Int::one(m_params); } Montgomery_Int x = m_g[scalar.get_substring(m_window_bits * (exp_nibbles - 1), m_window_bits)]; @@ -140,27 +134,37 @@ BigInt Montgomery_Exponentation_State::exponentiation_vartime(const BigInt& scal } CT::unpoison(x); - return x.value(); + return x; +} + +std::shared_ptr monty_precompute(const Montgomery_Int& g, + size_t window_bits, + bool const_time) { + return std::make_shared(g, window_bits, const_time); } std::shared_ptr monty_precompute( const std::shared_ptr& params, const BigInt& g, size_t window_bits, bool const_time) { - return std::make_shared(params, g, window_bits, const_time); + BOTAN_ARG_CHECK(g < params->p(), "Montgomery base too big"); + Montgomery_Int monty_g(params, g); + return monty_precompute(monty_g, window_bits, const_time); } -BigInt monty_execute(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k, size_t max_k_bits) { +Montgomery_Int monty_execute(const Montgomery_Exponentation_State& precomputed_state, + const BigInt& k, + size_t max_k_bits) { return precomputed_state.exponentiation(k, max_k_bits); } -BigInt monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k) { +Montgomery_Int monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k) { return precomputed_state.exponentiation_vartime(k); } -BigInt monty_multi_exp(const std::shared_ptr& params_p, - const BigInt& x_bn, - const BigInt& z1, - const BigInt& y_bn, - const BigInt& z2) { +Montgomery_Int monty_multi_exp(const std::shared_ptr& params_p, + const BigInt& x_bn, + const BigInt& z1, + const BigInt& y_bn, + const BigInt& z2) { if(z1.is_negative() || z2.is_negative()) { throw Invalid_Argument("multi_exponentiate exponents must be positive"); } @@ -225,7 +229,7 @@ BigInt monty_multi_exp(const std::shared_ptr& params_p, H.mul_by(*M[z12], ws); } - return H.value(); + return H; } } // namespace Botan diff --git a/src/lib/math/numbertheory/monty_exp.h b/src/lib/math/numbertheory/monty_exp.h index 95f003aa3da..b0ee87baf36 100644 --- a/src/lib/math/numbertheory/monty_exp.h +++ b/src/lib/math/numbertheory/monty_exp.h @@ -1,5 +1,5 @@ /* -* (C) 2018 Jack Lloyd +* (C) 2018,2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -7,15 +7,13 @@ #ifndef BOTAN_MONTY_EXP_H_ #define BOTAN_MONTY_EXP_H_ -#include +#include #include namespace Botan { +class BigInt; class Modular_Reducer; - -class Montgomery_Params; - class Montgomery_Exponentation_State; /* @@ -27,28 +25,37 @@ std::shared_ptr monty_precompute( size_t window_bits, bool const_time = true); +/* +* Precompute for calculating values g^x mod p +*/ +std::shared_ptr monty_precompute(const Montgomery_Int& g, + size_t window_bits, + bool const_time = true); + /* * Return g^k mod p */ -BigInt monty_execute(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k, size_t max_k_bits); +Montgomery_Int monty_execute(const Montgomery_Exponentation_State& precomputed_state, + const BigInt& k, + size_t max_k_bits); /* * Return g^k mod p taking variable time depending on k * @warning only use this if k is public */ -BigInt monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k); +Montgomery_Int monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k); -inline BigInt monty_exp(const std::shared_ptr& params_p, - const BigInt& g, - const BigInt& k, - size_t max_k_bits) { +inline Montgomery_Int monty_exp(const std::shared_ptr& params_p, + const BigInt& g, + const BigInt& k, + size_t max_k_bits) { auto precomputed = monty_precompute(params_p, g, 4, true); return monty_execute(*precomputed, k, max_k_bits); } -inline BigInt monty_exp_vartime(const std::shared_ptr& params_p, - const BigInt& g, - const BigInt& k) { +inline Montgomery_Int monty_exp_vartime(const std::shared_ptr& params_p, + const BigInt& g, + const BigInt& k) { auto precomputed = monty_precompute(params_p, g, 4, false); return monty_execute_vartime(*precomputed, k); } @@ -56,11 +63,11 @@ inline BigInt monty_exp_vartime(const std::shared_ptr& /** * Return (x^z1 * y^z2) % p */ -BigInt monty_multi_exp(const std::shared_ptr& params_p, - const BigInt& x, - const BigInt& z1, - const BigInt& y, - const BigInt& z2); +Montgomery_Int monty_multi_exp(const std::shared_ptr& params_p, + const BigInt& x, + const BigInt& z1, + const BigInt& y, + const BigInt& z2); } // namespace Botan diff --git a/src/lib/math/numbertheory/numthry.cpp b/src/lib/math/numbertheory/numthry.cpp index 18099865617..36074e2e1c7 100644 --- a/src/lib/math/numbertheory/numthry.cpp +++ b/src/lib/math/numbertheory/numthry.cpp @@ -44,7 +44,7 @@ BigInt sqrt_modulo_prime(const BigInt& a, const BigInt& p) { // If p == 3 (mod 4) there is a simple solution if(p % 4 == 3) { - return monty_exp_vartime(monty_p, a, ((p + 1) >> 2)); + return monty_exp_vartime(monty_p, a, ((p + 1) >> 2)).value(); } // Otherwise we have to use Shanks-Tonelli @@ -54,7 +54,7 @@ BigInt sqrt_modulo_prime(const BigInt& a, const BigInt& p) { q -= 1; q >>= 1; - BigInt r = monty_exp_vartime(monty_p, a, q); + BigInt r = monty_exp_vartime(monty_p, a, q).value(); BigInt n = mod_p.multiply(a, mod_p.square(r)); r = mod_p.multiply(r, a); @@ -81,7 +81,7 @@ BigInt sqrt_modulo_prime(const BigInt& a, const BigInt& p) { } } - BigInt c = monty_exp_vartime(monty_p, BigInt::from_word(z), (q << 1) + 1); + BigInt c = monty_exp_vartime(monty_p, BigInt::from_word(z), (q << 1) + 1).value(); while(n > 1) { q = n; @@ -97,7 +97,7 @@ BigInt sqrt_modulo_prime(const BigInt& a, const BigInt& p) { } BOTAN_ASSERT_NOMSG(s >= (i + 1)); // No underflow! - c = monty_exp_vartime(monty_p, c, BigInt::power_of_2(s - i - 1)); + c = monty_exp_vartime(monty_p, c, BigInt::power_of_2(s - i - 1)).value(); r = mod_p.multiply(r, c); c = mod_p.square(c); n = mod_p.multiply(n, c); @@ -299,7 +299,7 @@ BigInt power_mod(const BigInt& base, const BigInt& exp, const BigInt& mod) { if(mod.is_odd()) { auto monty_params = std::make_shared(mod, reduce_mod); - return monty_exp(monty_params, reduce_mod.reduce(base), exp, exp_bits); + return monty_exp(monty_params, reduce_mod.reduce(base), exp, exp_bits).value(); } /* diff --git a/src/lib/math/numbertheory/primality.cpp b/src/lib/math/numbertheory/primality.cpp index 9e27b44d77b..074c976ac70 100644 --- a/src/lib/math/numbertheory/primality.cpp +++ b/src/lib/math/numbertheory/primality.cpp @@ -122,7 +122,7 @@ bool passes_miller_rabin_test(const BigInt& n, auto powm_a_n = monty_precompute(monty_n, a, powm_window); - BigInt y = monty_execute(*powm_a_n, nm1_s, n_bits); + BigInt y = monty_execute(*powm_a_n, nm1_s, n_bits).value(); if(y == 1 || y == n_minus_1) { return true; diff --git a/src/lib/pubkey/dl_group/dl_group.cpp b/src/lib/pubkey/dl_group/dl_group.cpp index 9ad3c16ae18..87fb524432a 100644 --- a/src/lib/pubkey/dl_group/dl_group.cpp +++ b/src/lib/pubkey/dl_group/dl_group.cpp @@ -75,16 +75,18 @@ class DL_Group_Data final { size_t exponent_bits() const { return m_exponent_bits; } - BigInt power_g_p(const BigInt& k, size_t max_k_bits) const { return monty_execute(*m_monty, k, max_k_bits); } + BigInt power_g_p(const BigInt& k, size_t max_k_bits) const { + return monty_execute(*m_monty, k, max_k_bits).value(); + } - BigInt power_g_p_vartime(const BigInt& k) const { return monty_execute_vartime(*m_monty, k); } + BigInt power_g_p_vartime(const BigInt& k) const { return monty_execute_vartime(*m_monty, k).value(); } BigInt power_b_p(const BigInt& b, const BigInt& k, size_t max_k_bits) const { - return monty_exp(m_monty_params, b, k, max_k_bits); + return monty_exp(m_monty_params, b, k, max_k_bits).value(); } BigInt power_b_p_vartime(const BigInt& b, const BigInt& k) const { - return monty_exp_vartime(m_monty_params, b, k); + return monty_exp_vartime(m_monty_params, b, k).value(); } bool q_is_set() const { return m_q_bits > 0; } @@ -515,7 +517,7 @@ BigInt DL_Group::square_mod_q(const BigInt& x) const { } BigInt DL_Group::multi_exponentiate(const BigInt& x, const BigInt& y, const BigInt& z) const { - return monty_multi_exp(data().monty_params_p(), get_g(), x, y, z); + return monty_multi_exp(data().monty_params_p(), get_g(), x, y, z).value(); } BigInt DL_Group::power_g_p(const BigInt& x) const { diff --git a/src/lib/pubkey/elgamal/elgamal.cpp b/src/lib/pubkey/elgamal/elgamal.cpp index d19d9a2a722..02cb1862d66 100644 --- a/src/lib/pubkey/elgamal/elgamal.cpp +++ b/src/lib/pubkey/elgamal/elgamal.cpp @@ -140,7 +140,7 @@ std::vector ElGamal_Encryption_Operation::raw_encrypt(std::span& monty_n() const { return m_monty_n; } + private: BigInt m_n; BigInt m_e; @@ -68,10 +70,9 @@ class RSA_Private_Data final { m_d1(std::move(d1)), m_d2(std::move(d2)), m_c(std::move(c)), - m_mod_p(m_p), - m_mod_q(m_q), - m_monty_p(std::make_shared(m_p, m_mod_p)), - m_monty_q(std::make_shared(m_q, m_mod_q)), + m_monty_p(std::make_shared(m_p)), + m_monty_q(std::make_shared(m_q)), + m_c_monty(m_monty_p, m_c), m_p_bits(m_p.bits()), m_q_bits(m_q.bits()) {} @@ -87,9 +88,7 @@ class RSA_Private_Data final { const BigInt& get_c() const { return m_c; } - const Modular_Reducer& mod_p() const { return m_mod_p; } - - const Modular_Reducer& mod_q() const { return m_mod_q; } + const Montgomery_Int& get_c_monty() const { return m_c_monty; } const std::shared_ptr& monty_p() const { return m_monty_p; } @@ -99,6 +98,8 @@ class RSA_Private_Data final { size_t q_bits() const { return m_q_bits; } + bool primes_imbalanced() const { return p_bits() != q_bits(); } + private: BigInt m_d; BigInt m_p; @@ -107,10 +108,9 @@ class RSA_Private_Data final { BigInt m_d2; BigInt m_c; - Modular_Reducer m_mod_p; - Modular_Reducer m_mod_q; std::shared_ptr m_monty_p; std::shared_ptr m_monty_q; + Montgomery_Int m_c_monty; size_t m_p_bits; size_t m_q_bits; }; @@ -473,10 +473,12 @@ class RSA_Private_Operation { private: BigInt rsa_private_op(const BigInt& m) const { /* - TODO - Consider using Montgomery reduction instead of Barrett, using - the "Smooth RSA-CRT" method. https://eprint.iacr.org/2007/039.pdf + All normal implementations generate p/q of the same bitlength, + so this should rarely occur in practice */ + if(m_private->primes_imbalanced()) { + return monty_exp(m_public->monty_n(), m, m_private->get_d(), m_public->get_n().bits()).value(); + } static constexpr size_t powm_window = 4; @@ -498,8 +500,8 @@ class RSA_Private_Operation { auto future_j1 = Thread_Pool::global_instance().run([this, &m, &d1_mask]() { #endif const BigInt masked_d1 = m_private->get_d1() + (d1_mask * (m_private->get_p() - 1)); - auto powm_d1_p = monty_precompute(m_private->monty_p(), m_private->mod_p().reduce(m), powm_window); - BigInt j1 = monty_execute(*powm_d1_p, masked_d1, m_max_d1_bits); + auto powm_d1_p = monty_precompute(Montgomery_Int::from_wide_int(m_private->monty_p(), m), powm_window); + auto j1 = monty_execute(*powm_d1_p, masked_d1, m_max_d1_bits); #if defined(BOTAN_RSA_USE_ASYNC) return j1; @@ -508,11 +510,11 @@ class RSA_Private_Operation { const BigInt d2_mask(m_blinder.rng(), m_blinding_bits); const BigInt masked_d2 = m_private->get_d2() + (d2_mask * (m_private->get_q() - 1)); - auto powm_d2_q = monty_precompute(m_private->monty_q(), m_private->mod_q().reduce(m), powm_window); - const BigInt j2 = monty_execute(*powm_d2_q, masked_d2, m_max_d2_bits); + auto powm_d2_q = monty_precompute(Montgomery_Int::from_wide_int(m_private->monty_q(), m), powm_window); + const auto j2 = monty_execute(*powm_d2_q, masked_d2, m_max_d2_bits).value(); #if defined(BOTAN_RSA_USE_ASYNC) - BigInt j1 = future_j1.get(); + auto j1 = future_j1.get(); #endif /* @@ -521,16 +523,17 @@ class RSA_Private_Operation { * c = q^-1 mod p (this is precomputed) * h = c*(j1-j2) mod p * m = j2 + h*q - * - * We must avoid leaking if j1 >= j2 or not, as doing so allows deriving - * information about the secret prime. Do this by first adding p to j1, - * which should ensure the subtraction of j2 does not underflow. But - * this may still underflow if p and q are imbalanced in size. */ - j1 = - m_private->mod_p().multiply(m_private->mod_p().reduce((m_private->get_p() + j1) - j2), m_private->get_c()); - return j1 * m_private->get_q() + j2; + const auto j2_p = Montgomery_Int::from_wide_int(m_private->monty_p(), j2); + + /** + * This doesn't quite match up with the "Smooth-CRT" proposal; there we + * would multiply by c * R2 so would have the effect of both multiplying + * by c and immediately converting from Montgomery to standard form. + */ + j1 = (j1 - j2_p) * m_private->get_c_monty(); + return j1.value() * m_private->get_q() + j2; } std::shared_ptr m_public;