Skip to content

Commit

Permalink
Make more BigInt functions const-time
Browse files Browse the repository at this point in the history
In particular comparisons, calc sig words, and mod_sub are const time now.
  • Loading branch information
randombit committed Nov 27, 2018
1 parent a512d68 commit 00b6842
Show file tree
Hide file tree
Showing 7 changed files with 328 additions and 135 deletions.
104 changes: 55 additions & 49 deletions src/lib/math/bigint/big_ops2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,41 @@ BigInt& BigInt::mod_add(const BigInt& s, const BigInt& mod, secure_vector<word>&
if(this->is_negative() || s.is_negative() || mod.is_negative())
throw Invalid_Argument("BigInt::mod_add expects all arguments are positive");

// TODO add optimized version of this
*this += s;
this->reduce_below(mod, ws);
BOTAN_DEBUG_ASSERT(*this < mod);
BOTAN_DEBUG_ASSERT(s < mod);

/*
t + s or t + s - p == t - (p - s)
So first compute ws = p - s
Then compute t + s and t - ws
If t - ws does not borrow, then that is the correct valued
*/

const size_t mod_sw = mod.sig_words();
BOTAN_ARG_CHECK(mod_sw > 0, "BigInt::mod_add modulus must be positive");

this->grow_to(mod_sw);
s.grow_to(mod_sw);

// First mod_sw for p - s, 2*mod_sw for bigint_addsub workspace
if(ws.size() < 3*mod_sw)
ws.resize(3*mod_sw);

word borrow = bigint_sub3(&ws[0], mod.data(), mod_sw, s.data(), mod_sw);
CT::unpoison(borrow);
BOTAN_ASSERT_NOMSG(borrow == 0);

// Compute t - ws
borrow = bigint_sub3(&ws[mod_sw], this->data(), mod_sw, &ws[0], mod_sw);

// Compute t + s
bigint_add3_nc(&ws[mod_sw*2], this->data(), mod_sw, s.data(), mod_sw);

CT::conditional_copy_mem(borrow, &ws[0], &ws[mod_sw*2], &ws[mod_sw], mod_sw);
set_words(&ws[0], mod_sw);

return (*this);
}
Expand All @@ -126,50 +158,30 @@ BigInt& BigInt::mod_sub(const BigInt& s, const BigInt& mod, secure_vector<word>&
if(this->is_negative() || s.is_negative() || mod.is_negative())
throw Invalid_Argument("BigInt::mod_sub expects all arguments are positive");

const size_t mod_sw = mod.sig_words();

// We are assuming in this function that *this and s are no more than mod_sw words long
BOTAN_DEBUG_ASSERT(*this < mod);
BOTAN_DEBUG_ASSERT(s < mod);

// We are assuming here that *this and s are no more than mod_sw words long
const size_t t_w = std::min(mod_sw, size());
const size_t s_w = std::min(mod_sw, s.size());

/*
TODO make this const time
*/

int32_t relative_size = bigint_cmp(data(), t_w, s.data(), s_w);
const size_t mod_sw = mod.sig_words();

if(relative_size >= 0)
{
/*
this >= s in which case just subtract
this->grow_to(mod_sw);
s.grow_to(mod_sw);

Here s_w might be > t_w because these values are just based on
the size of the buffer. But we know that because *this < s, then
this->sig_words() must be <= s.sig_words() so set the size of s
to the minimum of t and s words.
*/
BOTAN_DEBUG_ASSERT(sig_words() <= s.sig_words());
bigint_sub2(mutable_data(), t_w, s.data(), std::min(t_w, s_w));
}
else
{
// Otherwise we must sub s and then add p (or add (p - s) as here)
if(ws.size() < mod_sw)
ws.resize(mod_sw);

if(ws.size() < mod_sw)
ws.resize(mod_sw);
// is t < s or not?
const word is_lt = bigint_ct_is_lt(data(), mod_sw, s.data(), mod_sw);

word borrow = bigint_sub3(ws.data(), mod.data(), mod_sw, s.data(), s_w);
BOTAN_ASSERT_NOMSG(borrow == 0);
// ws = p - s
word borrow = bigint_sub3(ws.data(), mod.data(), mod_sw, s.data(), mod_sw);
CT::unpoison(borrow);
BOTAN_ASSERT_NOMSG(borrow == 0);

if(size() < mod_sw)
grow_to(mod_sw);

word carry = bigint_add2_nc(mutable_data(), size(), ws.data(), mod_sw);
BOTAN_ASSERT_NOMSG(carry == 0);
}
// Compute either (t - s) or (t + (p - s)) depending on mask
word carry = bigint_cnd_addsub(is_lt, mutable_data(), ws.data(), s.data(), mod_sw);
CT::unpoison(carry);
BOTAN_ASSERT_NOMSG(carry == 0);

return (*this);
}
Expand All @@ -185,24 +197,18 @@ BigInt& BigInt::rev_sub(const word y[], size_t y_sw, secure_vector<word>& ws)

const size_t x_sw = this->sig_words();

const int32_t relative_size = bigint_cmp(y, y_sw, this->data(), x_sw);
// TODO use bigint_sub_abs or a new variant of it

ws.resize(std::max(y_sw, x_sw) + 1);
clear_mem(ws.data(), ws.size());

if(relative_size < 0)
word borrow = bigint_sub3(ws.data(), y, y_sw, this->data(), x_sw);

if(borrow)
{
bigint_sub3(ws.data(), this->data(), x_sw, y, y_sw);
this->flip_sign();
}
else if(relative_size == 0)
{
ws.clear();
}
else if(relative_size > 0)
{
bigint_sub3(ws.data(), y, y_sw, this->data(), x_sw);
}

this->swap_reg(ws);

Expand Down
68 changes: 51 additions & 17 deletions src/lib/math/bigint/bigint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,33 @@ int32_t BigInt::cmp(const BigInt& other, bool check_signs) const
other.data(), other.sig_words());
}

bool BigInt::is_equal(const BigInt& other) const
{
if(this->sign() != other.sign())
return false;

return bigint_ct_is_eq(this->data(), this->sig_words(),
other.data(), other.sig_words());
}

bool BigInt::is_less_than(const BigInt& other) const
{
if(this->is_negative() && other.is_positive())
return true;

if(this->is_positive() && other.is_negative())
return false;

if(other.is_negative() && this->is_negative())
{
return !bigint_ct_is_lt(other.data(), other.sig_words(),
this->data(), this->sig_words(), true);
}

return bigint_ct_is_lt(this->data(), this->sig_words(),
other.data(), other.sig_words());
}

void BigInt::encode_words(word out[], size_t size) const
{
const size_t words = sig_words();
Expand All @@ -155,25 +182,21 @@ size_t BigInt::Data::calc_sig_words() const
{
size_t sig = m_reg.size();

#if 0
// Const time, but slower ...

word seen_only_zeros = MP_WORD_MASK;
word sub = 1;

for(size_t i = 0; i != m_reg.size(); ++i)
{
const word w = m_reg[m_reg.size() - i - 1];
seen_only_zeros &= CT::is_zero(w);
sub &= seen_only_zeros;

sub &= CT::is_zero(w);
sig -= sub;
}

#else
while(sig && (m_reg[sig-1] == 0))
sig--;
#endif
/*
* This depends on the data so is poisoned, but unpoison it here as
* later conditionals are made on the size.
*/
CT::unpoison(sig);

return sig;
}

Expand Down Expand Up @@ -221,10 +244,14 @@ uint32_t BigInt::to_u32bit() const
void BigInt::set_bit(size_t n)
{
const size_t which = n / BOTAN_MP_WORD_BITS;
const word mask = static_cast<word>(1) << (n % BOTAN_MP_WORD_BITS);
if(which >= size()) grow_to(which + 1);

m_data.set_word_at(which, m_data.get_word_at(which) | mask);
if(which >= size())
{
grow_to(which + 1);
}

const word mask = static_cast<word>(1) << (n % BOTAN_MP_WORD_BITS);
m_data.set_word_at(which, word_at(which) | mask);
}

/*
Expand All @@ -233,9 +260,12 @@ void BigInt::set_bit(size_t n)
void BigInt::clear_bit(size_t n)
{
const size_t which = n / BOTAN_MP_WORD_BITS;
const word mask = ~(static_cast<word>(1) << (n % BOTAN_MP_WORD_BITS));

if(which < size())
m_data.set_word_at(which, m_data.get_word_at(which) & mask);
{
const word mask = ~(static_cast<word>(1) << (n % BOTAN_MP_WORD_BITS));
m_data.set_word_at(which, word_at(which) & mask);
}
}

size_t BigInt::bytes() const
Expand All @@ -254,7 +284,10 @@ size_t BigInt::bits() const
return 0;

const size_t full_words = words - 1;
return (full_words * BOTAN_MP_WORD_BITS + high_bit(word_at(full_words)));
const size_t bits = (full_words * BOTAN_MP_WORD_BITS + high_bit(word_at(full_words)));
// Need to unpoison due to high_bit not being const time
CT::unpoison(bits);
return bits;
}

/*
Expand Down Expand Up @@ -303,6 +336,7 @@ void BigInt::reduce_below(const BigInt& p, secure_vector<word>& ws)
{
word borrow = bigint_sub3(ws.data(), data(), p_words + 1, p.data(), p_words);

//CT::unpoison(borrow); // fixme
if(borrow)
break;

Expand Down
30 changes: 22 additions & 8 deletions src/lib/math/bigint/bigint.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ class BOTAN_PUBLIC_API(2,0) BigInt final
*
* Assumes that *this is (if anything) only slightly larger than
* mod and performs repeated subtractions. It should not be used if
* *this is much larger than mod, instead of modulo operator.
* *this is much larger than mod, instead use modulo operator.
*/
void reduce_below(const BigInt& mod, secure_vector<word> &ws);

Expand All @@ -333,6 +333,20 @@ class BOTAN_PUBLIC_API(2,0) BigInt final
*/
int32_t cmp(const BigInt& n, bool check_signs = true) const;

/**
* Compare this to another BigInt
* @param n the BigInt value to compare with
* @result true if this == n or false otherwise
*/
bool is_equal(const BigInt& n) const;

/**
* Compare this to another BigInt
* @param n the BigInt value to compare with
* @result true if this < n or false otherwise
*/
bool is_less_than(const BigInt& n) const;

/**
* Compare this to an integer
* @param n the value to compare with
Expand Down Expand Up @@ -562,7 +576,7 @@ class BOTAN_PUBLIC_API(2,0) BigInt final
* Increase internal register buffer to at least n words
* @param n new size of register
*/
void grow_to(size_t n) { m_data.grow_to(n); }
void grow_to(size_t n) const { m_data.grow_to(n); }

/**
* Resize the vector to the minimum word size to hold the integer, or
Expand Down Expand Up @@ -896,7 +910,7 @@ class BOTAN_PUBLIC_API(2,0) BigInt final
}
}

void grow_to(size_t n)
void grow_to(size_t n) const
{
if(n > size())
{
Expand Down Expand Up @@ -954,7 +968,7 @@ class BOTAN_PUBLIC_API(2,0) BigInt final

size_t calc_sig_words() const;

secure_vector<word> m_reg;
mutable secure_vector<word> m_reg;
mutable size_t m_sig_words = sig_words_npos;
};

Expand Down Expand Up @@ -986,17 +1000,17 @@ BigInt BOTAN_PUBLIC_API(2,0) operator>>(const BigInt& x, size_t n);
* Comparison Operators
*/
inline bool operator==(const BigInt& a, const BigInt& b)
{ return (a.cmp(b) == 0); }
{ return a.is_equal(b); }
inline bool operator!=(const BigInt& a, const BigInt& b)
{ return (a.cmp(b) != 0); }
{ return !a.is_equal(b); }
inline bool operator<=(const BigInt& a, const BigInt& b)
{ return (a.cmp(b) <= 0); }
inline bool operator>=(const BigInt& a, const BigInt& b)
{ return (a.cmp(b) >= 0); }
inline bool operator<(const BigInt& a, const BigInt& b)
{ return (a.cmp(b) < 0); }
{ return a.is_less_than(b); }
inline bool operator>(const BigInt& a, const BigInt& b)
{ return (a.cmp(b) > 0); }
{ return b.is_less_than(a); }

inline bool operator==(const BigInt& a, word b)
{ return (a.cmp_word(b) == 0); }
Expand Down
Loading

0 comments on commit 00b6842

Please sign in to comment.