diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index 3b70ecdc2b..8a98f0c706 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -75,12 +75,12 @@ UnsignedBigInteger UnsignedBigInteger::sub(const UnsignedBigInteger& other) cons UnsignedBigInteger result; if (*this < other) { - dbg() << "WARNING: bigint subtraction creates a negative number!"; return UnsignedBigInteger::create_invalid(); } u8 borrow = 0; for (size_t i = 0; i < other.length(); ++i) { + // This assertion should not fail, because we verified that *this>other at the beginning of the function ASSERT(!(borrow == 1 && m_words[i] == 0)); if (m_words[i] - borrow < other.m_words[i]) { @@ -128,6 +128,47 @@ UnsignedBigInteger UnsignedBigInteger::multiply(const UnsignedBigInteger& other) return result; } +/** + * Complexity: O(N^2) where N is the number of words in the larger number + * Division method: + * We loop over the bits of the divisor, attempting to subtract divisor<= 0; --word_index) { + for (int bit_index = UnsignedBigInteger::BITS_IN_WORD - 1; bit_index >= 0; --bit_index) { + + const size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index; + UnsignedBigInteger divisor_shifted = divisor.shift_left(shift_amount); + + UnsignedBigInteger temp_subtraction_result = leftover_dividend.sub(divisor_shifted); + if (!temp_subtraction_result.is_invalid()) { + leftover_dividend = temp_subtraction_result; + quotient.set_bit_inplace(shift_amount); + } + } + } + return UnsignedDivisionResult { quotient, leftover_dividend }; +} + +void UnsignedBigInteger::set_bit_inplace(size_t bit_index) +{ + const size_t word_index = bit_index / UnsignedBigInteger::BITS_IN_WORD; + const size_t inner_word_index = bit_index % UnsignedBigInteger::BITS_IN_WORD; + + for (size_t i = length(); i <= word_index; ++i) { + m_words.append(0); + } + m_words[word_index] |= (1 << inner_word_index); +} + UnsignedBigInteger UnsignedBigInteger::shift_left(size_t num_bits) const { // We can only do shift operations on individual words @@ -213,12 +254,16 @@ bool UnsignedBigInteger::operator<(const UnsignedBigInteger& other) const return false; } - size_t length = trimmed_length(); + int length = trimmed_length(); if (length == 0) { return false; } - - return m_words[length - 1] < other.m_words[length - 1]; + for (int i = length - 1; i >= 0; --i) { + if (m_words[i] == other.m_words[i]) + continue; + return m_words[i] < other.m_words[i]; + } + return false; } size_t UnsignedBigInteger::trimmed_length() const @@ -237,5 +282,4 @@ UnsignedBigInteger UnsignedBigInteger::create_invalid() invalid.invalidate(); return invalid; } - } diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index ce8e331737..d412bd41bf 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -30,6 +30,9 @@ #include namespace Crypto { + +struct UnsignedDivisionResult; + class UnsignedBigInteger { public: UnsignedBigInteger(u32 x) { m_words.append(x); } @@ -49,7 +52,10 @@ public: UnsignedBigInteger sub(const UnsignedBigInteger& other) const; UnsignedBigInteger multiply(const UnsignedBigInteger& other) const; UnsignedBigInteger shift_left(size_t num_bits) const; - UnsignedBigInteger shift_left_by_n_words(const size_t number_of_words) const; + + UnsignedDivisionResult divide(const UnsignedBigInteger& divisor) const; + + void set_bit_inplace(size_t bit_index); size_t length() const { return m_words.size(); } @@ -63,6 +69,7 @@ public: bool is_invalid() const { return m_is_invalid; } private: + UnsignedBigInteger shift_left_by_n_words(const size_t number_of_words) const; u32 shift_left_get_one_word(const size_t num_bits, const size_t result_word_index) const; static constexpr size_t BITS_IN_WORD = 32; @@ -72,6 +79,11 @@ private: bool m_is_invalid { false }; }; +struct UnsignedDivisionResult { + Crypto::UnsignedBigInteger quotient; + Crypto::UnsignedBigInteger remainder; +}; + } inline const LogStream& operator<<(const LogStream& stream, const Crypto::UnsignedBigInteger value) diff --git a/Userland/test-crypto.cpp b/Userland/test-crypto.cpp index c8e9759bde..f3e8b8598f 100644 --- a/Userland/test-crypto.cpp +++ b/Userland/test-crypto.cpp @@ -306,6 +306,7 @@ void bigint_test_fibo500(); void bigint_addition_edgecases(); void bigint_subtraction(); void bigint_multiplication(); +void bigint_division(); int aes_cbc_tests() { @@ -801,6 +802,7 @@ int bigint_tests() bigint_addition_edgecases(); bigint_subtraction(); bigint_multiplication(); + bigint_division(); return 0; } @@ -905,6 +907,14 @@ void bigint_subtraction() FAIL(Incorrect Result); } } + { + I_TEST((BigInteger | Subtraction with large numbers 2)); + Crypto::UnsignedBigInteger num1(Vector { 1483061863, 446680044, 1123294122, 191895498, 3347106536, 16, 0, 0, 0 }); + Crypto::UnsignedBigInteger num2(Vector { 4196414175, 1117247942, 1123294122, 191895498, 3347106536, 16 }); + Crypto::UnsignedBigInteger result = num1.sub(num2); + // this test only verifies that we don't crash on an assertion + PASS; + } } void bigint_multiplication() @@ -944,3 +954,44 @@ void bigint_multiplication() } } } +void bigint_division() +{ + { + I_TEST((BigInteger | Simple Division)); + Crypto::UnsignedBigInteger num1(27194); + Crypto::UnsignedBigInteger num2(251); + auto result = num1.divide(num2); + Crypto::UnsignedDivisionResult expected = { Crypto::UnsignedBigInteger(108), Crypto::UnsignedBigInteger(86) }; + if (result.quotient == expected.quotient && result.remainder == expected.remainder) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Division with big numbers)); + Crypto::UnsignedBigInteger num1 = bigint_fibonacci(386); + Crypto::UnsignedBigInteger num2 = bigint_fibonacci(238); + auto result = num1.divide(num2); + Crypto::UnsignedDivisionResult expected = { + Crypto::UnsignedBigInteger(Vector { 2300984486, 2637503534, 2022805584, 107 }), + Crypto::UnsignedBigInteger(Vector { 1483061863, 446680044, 1123294122, 191895498, 3347106536, 16, 0, 0, 0 }) + }; + if (result.quotient == expected.quotient && result.remainder == expected.remainder) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Combined test)); + auto num1 = bigint_fibonacci(497); + auto num2 = bigint_fibonacci(238); + auto div_result = num1.divide(num2); + if (div_result.quotient.multiply(num2).add(div_result.remainder) == num1) { + PASS; + } else { + FAIL(Incorrect Result); + } + } +}