LibCrypto: Do not allow signed big integers to be negative zero

If a big integer were to become negative zero, set the sign to instead
be positive. This prevents odd scenarios where users of signed big ints
would falsely think the result of some big int arithmetic is negative.
This commit is contained in:
Timothy Flynn 2022-02-06 13:17:34 +00:00 committed by Linus Groh
parent 4d785b9aa0
commit b0d6399f60
Notes: sideshowbarker 2024-07-17 19:42:54 +09:00
4 changed files with 45 additions and 2 deletions

View file

@ -642,3 +642,19 @@ TEST_CASE(test_signed_multiplication_with_two_big_numbers)
EXPECT_EQ(result.unsigned_value().words(), expected_results); EXPECT_EQ(result.unsigned_value().words(), expected_results);
EXPECT(result.is_negative()); EXPECT(result.is_negative());
} }
TEST_CASE(test_negative_zero_is_not_allowed)
{
Crypto::SignedBigInteger zero(Crypto::UnsignedBigInteger(0), true);
EXPECT(!zero.is_negative());
zero.negate();
EXPECT(!zero.is_negative());
Crypto::SignedBigInteger positive_five(Crypto::UnsignedBigInteger(5), false);
Crypto::SignedBigInteger negative_five(Crypto::UnsignedBigInteger(5), true);
zero = positive_five.plus(negative_five);
EXPECT(zero.unsigned_value().is_zero());
EXPECT(!zero.is_negative());
}

View file

@ -25,6 +25,7 @@ public:
: m_sign(sign) : m_sign(sign)
, m_unsigned_data(move(unsigned_data)) , m_unsigned_data(move(unsigned_data))
{ {
ensure_sign_is_valid();
} }
explicit SignedBigInteger(UnsignedBigInteger unsigned_data) explicit SignedBigInteger(UnsignedBigInteger unsigned_data)
@ -72,9 +73,18 @@ public:
const Vector<u32, STARTING_WORD_SIZE> words() const { return m_unsigned_data.words(); } const Vector<u32, STARTING_WORD_SIZE> words() const { return m_unsigned_data.words(); }
bool is_negative() const { return m_sign; } bool is_negative() const { return m_sign; }
void negate() { m_sign = !m_sign; } void negate()
{
if (!m_unsigned_data.is_zero())
m_sign = !m_sign;
}
void set_to_0()
{
m_unsigned_data.set_to_0();
m_sign = false;
}
void set_to_0() { m_unsigned_data.set_to_0(); }
void set_to(i32 other) void set_to(i32 other)
{ {
m_unsigned_data.set_to((u32)other); m_unsigned_data.set_to((u32)other);
@ -129,6 +139,12 @@ public:
bool operator>(const UnsignedBigInteger& other) const; bool operator>(const UnsignedBigInteger& other) const;
private: private:
void ensure_sign_is_valid()
{
if (m_sign && m_unsigned_data.is_zero())
m_sign = false;
}
bool m_sign { false }; bool m_sign { false };
UnsignedBigInteger m_unsigned_data; UnsignedBigInteger m_unsigned_data;
}; };

View file

@ -145,6 +145,16 @@ void UnsignedBigInteger::set_to(const UnsignedBigInteger& other)
m_cached_hash = 0; m_cached_hash = 0;
} }
bool UnsignedBigInteger::is_zero() const
{
for (size_t i = 0; i < length(); ++i) {
if (m_words[i] != 0)
return false;
}
return true;
}
size_t UnsignedBigInteger::trimmed_length() const size_t UnsignedBigInteger::trimmed_length() const
{ {
if (!m_cached_trimmed_length.has_value()) { if (!m_cached_trimmed_length.has_value()) {

View file

@ -72,6 +72,7 @@ public:
m_cached_hash = 0; m_cached_hash = 0;
} }
bool is_zero() const;
bool is_odd() const { return m_words.size() && (m_words[0] & 1); } bool is_odd() const { return m_words.size() && (m_words[0] & 1); }
bool is_invalid() const { return m_is_invalid; } bool is_invalid() const { return m_is_invalid; }