Skip to content

Commit

Permalink
Merge pull request #4558 from randombit/jack/stay-in-monty
Browse files Browse the repository at this point in the history
Modify Montgomery exponetiation to return results in Montgomery form
  • Loading branch information
randombit authored Jan 17, 2025
2 parents d5d1a38 + d0ecb8c commit 5826859
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 93 deletions.
5 changes: 5 additions & 0 deletions doc/deprecated.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
28 changes: 23 additions & 5 deletions src/lib/math/numbertheory/monty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,18 @@ void Montgomery_Params::square_this(BigInt& x, secure_vector<word>& ws) const {
copy_mem(x.mutable_data(), z_data, output_size);
}

Montgomery_Int Montgomery_Int::one(const std::shared_ptr<const Montgomery_Params>& params) {
return Montgomery_Int(params, params->R1(), false);
}

Montgomery_Int Montgomery_Int::from_wide_int(const std::shared_ptr<const Montgomery_Params>& params, const BigInt& x) {
//BOTAN_ARG_CHECK(x < params->p() * params->p(), "Input too large");

secure_vector<word> 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<const Montgomery_Params>& params,
const BigInt& v,
bool redc_needed) :
Expand Down Expand Up @@ -301,11 +313,7 @@ Montgomery_Int::Montgomery_Int(std::shared_ptr<const Montgomery_Params> 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);
}

Expand Down Expand Up @@ -335,49 +343,58 @@ 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<word> ws;
BigInt z = m_v;
z.mod_add(other.m_v, m_params->p(), ws);
return Montgomery_Int(m_params, z, false);
}

Montgomery_Int Montgomery_Int::operator-(const Montgomery_Int& other) const {
BOTAN_STATE_CHECK(other.m_params == m_params);
secure_vector<word> ws;
BigInt z = m_v;
z.mod_sub(other.m_v, m_params->p(), ws);
return Montgomery_Int(m_params, z, false);
}

Montgomery_Int& Montgomery_Int::operator+=(const Montgomery_Int& other) {
BOTAN_STATE_CHECK(other.m_params == m_params);
secure_vector<word> ws;
return this->add(other, ws);
}

Montgomery_Int& Montgomery_Int::add(const Montgomery_Int& other, secure_vector<word>& 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<word> ws;
return this->sub(other, ws);
}

Montgomery_Int& Montgomery_Int::sub(const Montgomery_Int& other, secure_vector<word>& 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<word> 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<word>& 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<word>& ws) {
BOTAN_STATE_CHECK(other.m_params == m_params);
m_params->mul_by(m_v, other.m_v, ws);
return (*this);
}
Expand All @@ -388,6 +405,7 @@ Montgomery_Int& Montgomery_Int::mul_by(const secure_vector<word>& other, secure_
}

Montgomery_Int& Montgomery_Int::operator*=(const Montgomery_Int& other) {
BOTAN_STATE_CHECK(other.m_params == m_params);
secure_vector<word> ws;
return mul_by(other, ws);
}
Expand Down
9 changes: 9 additions & 0 deletions src/lib/math/numbertheory/monty.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<const Montgomery_Params>& params);

/**
* Wide reduction - input can be at most 2*bytes long
*/
static Montgomery_Int from_wide_int(const std::shared_ptr<const Montgomery_Params>& params, const BigInt& x);

bool operator==(const Montgomery_Int& other) const;

bool operator!=(const Montgomery_Int& other) const { return (m_v != other.m_v); }
Expand Down Expand Up @@ -118,6 +125,8 @@ class BOTAN_TEST_API Montgomery_Int final {

void _const_time_unpoison() const { CT::unpoison(m_v); }

const std::shared_ptr<const Montgomery_Params>& _params() const { return m_params; }

private:
std::shared_ptr<const Montgomery_Params> m_params;
BigInt m_v;
Expand Down
66 changes: 35 additions & 31 deletions src/lib/math/numbertheory/monty_exp.cpp
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -18,28 +18,22 @@ namespace Botan {

class Montgomery_Exponentation_State final {
public:
Montgomery_Exponentation_State(const std::shared_ptr<const Montgomery_Params>& 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<const Montgomery_Params> m_params;
std::vector<Montgomery_Int> m_g;
size_t m_window_bits;
};

Montgomery_Exponentation_State::Montgomery_Exponentation_State(const std::shared_ptr<const Montgomery_Params>& 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");
}
Expand All @@ -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) {
Expand Down Expand Up @@ -93,14 +87,14 @@ void const_time_lookup(secure_vector<word>& output, const std::vector<Montgomery

} // namespace

BigInt Montgomery_Exponentation_State::exponentiation(const BigInt& scalar, size_t max_k_bits) const {
Montgomery_Int Montgomery_Exponentation_State::exponentiation(const BigInt& scalar, size_t max_k_bits) const {
BOTAN_DEBUG_ASSERT(scalar.bits() <= max_k_bits);
// TODO add a const-time implementation of above assert and use it in release builds

const size_t exp_nibbles = (max_k_bits + m_window_bits - 1) / m_window_bits;

if(exp_nibbles == 0) {
return BigInt::one();
return Montgomery_Int::one(m_params);
}

secure_vector<word> e_bits(m_params->p_words());
Expand All @@ -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<word> 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)];
Expand All @@ -140,27 +134,37 @@ BigInt Montgomery_Exponentation_State::exponentiation_vartime(const BigInt& scal
}

CT::unpoison(x);
return x.value();
return x;
}

std::shared_ptr<const Montgomery_Exponentation_State> monty_precompute(const Montgomery_Int& g,
size_t window_bits,
bool const_time) {
return std::make_shared<const Montgomery_Exponentation_State>(g, window_bits, const_time);
}

std::shared_ptr<const Montgomery_Exponentation_State> monty_precompute(
const std::shared_ptr<const Montgomery_Params>& params, const BigInt& g, size_t window_bits, bool const_time) {
return std::make_shared<const Montgomery_Exponentation_State>(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<const Montgomery_Params>& 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<const Montgomery_Params>& 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");
}
Expand Down Expand Up @@ -225,7 +229,7 @@ BigInt monty_multi_exp(const std::shared_ptr<const Montgomery_Params>& params_p,
H.mul_by(*M[z12], ws);
}

return H.value();
return H;
}

} // namespace Botan
45 changes: 26 additions & 19 deletions src/lib/math/numbertheory/monty_exp.h
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
/*
* (C) 2018 Jack Lloyd
* (C) 2018,2025 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef BOTAN_MONTY_EXP_H_
#define BOTAN_MONTY_EXP_H_

#include <botan/bigint.h>
#include <botan/internal/monty.h>
#include <memory>

namespace Botan {

class BigInt;
class Modular_Reducer;

class Montgomery_Params;

class Montgomery_Exponentation_State;

/*
Expand All @@ -27,40 +25,49 @@ std::shared_ptr<const Montgomery_Exponentation_State> monty_precompute(
size_t window_bits,
bool const_time = true);

/*
* Precompute for calculating values g^x mod p
*/
std::shared_ptr<const Montgomery_Exponentation_State> 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<const Montgomery_Params>& params_p,
const BigInt& g,
const BigInt& k,
size_t max_k_bits) {
inline Montgomery_Int monty_exp(const std::shared_ptr<const Montgomery_Params>& 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<const Montgomery_Params>& params_p,
const BigInt& g,
const BigInt& k) {
inline Montgomery_Int monty_exp_vartime(const std::shared_ptr<const Montgomery_Params>& params_p,
const BigInt& g,
const BigInt& k) {
auto precomputed = monty_precompute(params_p, g, 4, false);
return monty_execute_vartime(*precomputed, k);
}

/**
* Return (x^z1 * y^z2) % p
*/
BigInt monty_multi_exp(const std::shared_ptr<const Montgomery_Params>& params_p,
const BigInt& x,
const BigInt& z1,
const BigInt& y,
const BigInt& z2);
Montgomery_Int monty_multi_exp(const std::shared_ptr<const Montgomery_Params>& params_p,
const BigInt& x,
const BigInt& z1,
const BigInt& y,
const BigInt& z2);

} // namespace Botan

Expand Down
Loading

0 comments on commit 5826859

Please sign in to comment.