diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp index cd88a94603..e7f6c0bff7 100644 --- a/Userland/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp @@ -199,8 +199,7 @@ FLATTEN void UnsignedBigIntegerAlgorithms::shift_left_without_allocation( temp_plus.set_to_0(); temp_plus.m_words.append(carry_word); shift_left_by_n_words(temp_plus, temp_result.length(), temp_result); - add_without_allocation(output, temp_result, temp_plus); - output.set_to(temp_plus); + add_into_accumulator_without_allocation(output, temp_result); } } diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp index 49ddfbd623..b494db3b4e 100644 --- a/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp @@ -16,7 +16,6 @@ void UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation( UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_3, UnsignedBigInteger& temp_4, - UnsignedBigInteger& temp_plus, UnsignedBigInteger& temp_minus, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_d, @@ -30,8 +29,7 @@ void UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation( temp_u.set_to(a); if (a.words()[0] % 2 == 0) { // u += b - add_without_allocation(temp_u, b, temp_plus); - temp_u.set_to(temp_plus); + add_into_accumulator_without_allocation(temp_u, b); } temp_v.set_to(b); @@ -47,14 +45,12 @@ void UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation( temp_u.set_to(temp_minus); // d += x - add_without_allocation(temp_d, temp_x, temp_plus); - temp_d.set_to(temp_plus); + add_into_accumulator_without_allocation(temp_d, temp_x); while (temp_u.words()[0] % 2 == 0) { if (temp_d.words()[0] % 2 == 1) { // d += b - add_without_allocation(temp_d, b, temp_plus); - temp_d.set_to(temp_plus); + add_into_accumulator_without_allocation(temp_d, b); } // u /= 2 @@ -72,14 +68,12 @@ void UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation( temp_v.set_to(temp_minus); // x += d - add_without_allocation(temp_x, temp_d, temp_plus); - temp_x.set_to(temp_plus); + add_into_accumulator_without_allocation(temp_x, temp_d); while (temp_v.words()[0] % 2 == 0) { if (temp_x.words()[0] % 2 == 1) { // x += b - add_without_allocation(temp_x, b, temp_plus); - temp_x.set_to(temp_plus); + add_into_accumulator_without_allocation(temp_x, b); } // v /= 2 diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp index e6a1b44fcc..a990313fe0 100644 --- a/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp @@ -26,7 +26,7 @@ void UnsignedBigIntegerAlgorithms::destructive_modular_power_without_allocation( while (!(ep < 1)) { if (ep.words()[0] % 2 == 1) { // exp = (exp * base) % m; - multiply_without_allocation(exp, base, temp_1, temp_2, temp_3, temp_4, temp_multiply); + multiply_without_allocation(exp, base, temp_1, temp_2, temp_3, temp_multiply); divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); exp.set_to(temp_remainder); } @@ -36,7 +36,7 @@ void UnsignedBigIntegerAlgorithms::destructive_modular_power_without_allocation( ep.set_to(temp_quotient); // base = (base * base) % m; - multiply_without_allocation(base, base, temp_1, temp_2, temp_3, temp_4, temp_multiply); + multiply_without_allocation(base, base, temp_1, temp_2, temp_3, temp_multiply); divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); base.set_to(temp_remainder); diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp index eece4430b1..01bed4f581 100644 --- a/Userland/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp @@ -23,7 +23,6 @@ FLATTEN void UnsignedBigIntegerAlgorithms::multiply_without_allocation( UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, - UnsignedBigInteger& temp_plus, UnsignedBigInteger& output) { output.set_to_0(); @@ -39,8 +38,7 @@ FLATTEN void UnsignedBigIntegerAlgorithms::multiply_without_allocation( // output += (right << shift_amount); shift_left_without_allocation(right, shift_amount, temp_shift_result, temp_shift_plus, temp_shift); - add_without_allocation(output, temp_shift, temp_plus); - output.set_to(temp_plus); + add_into_accumulator_without_allocation(output, temp_shift); } } } diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/SimpleOperations.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/SimpleOperations.cpp index b5b7afbdc4..bf8e784180 100644 --- a/Userland/Libraries/LibCrypto/BigInt/Algorithms/SimpleOperations.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/SimpleOperations.cpp @@ -20,36 +20,51 @@ void UnsignedBigIntegerAlgorithms::add_without_allocation( const UnsignedBigInteger* const longer = (left.length() > right.length()) ? &left : &right; const UnsignedBigInteger* const shorter = (longer == &right) ? &left : &right; - u8 carry = 0; + output.set_to(*longer); + add_into_accumulator_without_allocation(output, *shorter); +} - output.set_to_0(); - output.m_words.resize_and_keep_capacity(longer->length()); +/** + * Complexity: O(N) where N is the number of words in the larger number + */ +void UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(UnsignedBigInteger& accumulator, UnsignedBigInteger const& value) +{ + auto value_length = value.trimmed_length(); - for (size_t i = 0; i < shorter->length(); ++i) { - u32 word_addition_result = shorter->m_words[i] + longer->m_words[i]; - u8 carry_out = 0; - // if there was a carry, the result will be smaller than any of the operands - if (word_addition_result + carry < shorter->m_words[i]) { - carry_out = 1; + // If needed, resize the accumulator so it can fit the value. + accumulator.resize_with_leading_zeros(value_length); + auto final_length = accumulator.length(); + + // Add the words of the value into the accumulator, rippling any carry as we go + UnsignedBigInteger::Word last_carry_for_word = 0; + for (size_t i = 0; i < value_length; ++i) { + UnsignedBigInteger::Word current_carry_for_word = 0; + if (Checked::addition_would_overflow(value.m_words[i], accumulator.m_words[i])) { + current_carry_for_word = 1; } - if (carry) { - word_addition_result++; + UnsignedBigInteger::Word word_addition_result = value.m_words[i] + accumulator.m_words[i]; + if (Checked::addition_would_overflow(word_addition_result, last_carry_for_word)) { + current_carry_for_word = 1; } - carry = carry_out; - output.m_words[i] = word_addition_result; + word_addition_result += last_carry_for_word; + last_carry_for_word = current_carry_for_word; + accumulator.m_words[i] = word_addition_result; } - for (size_t i = shorter->length(); i < longer->length(); ++i) { - u32 word_addition_result = longer->m_words[i] + carry; - - carry = 0; - if (word_addition_result < longer->m_words[i]) { - carry = 1; + // Ripple the carry over the remaining words in the accumulator until either there is no carry left or we run out of words + while (last_carry_for_word && final_length > value_length) { + UnsignedBigInteger::Word current_carry_for_word = 0; + if (Checked::addition_would_overflow(accumulator.m_words[value_length], last_carry_for_word)) { + current_carry_for_word = 1; } - output.m_words[i] = word_addition_result; + accumulator.m_words[value_length] += last_carry_for_word; + last_carry_for_word = current_carry_for_word; + value_length++; } - if (carry) { - output.m_words.append(carry); + + if (last_carry_for_word) { + // Note : The accumulator couldn't add the carry directly, so we reached its end + accumulator.m_words.append(last_carry_for_word); } } diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h b/Userland/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h index 29a16ce19a..3e645bcc9a 100644 --- a/Userland/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h @@ -13,18 +13,19 @@ namespace Crypto { class UnsignedBigIntegerAlgorithms { public: static void add_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output); + static void add_into_accumulator_without_allocation(UnsignedBigInteger& accumulator, UnsignedBigInteger const& value); static void subtract_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output); static void bitwise_or_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output); static void bitwise_and_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output); static void bitwise_xor_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output); static void bitwise_not_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger& output); static void shift_left_without_allocation(UnsignedBigInteger const& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output); - static void multiply_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output); + static void multiply_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& output); static void divide_without_allocation(UnsignedBigInteger const& numerator, UnsignedBigInteger const& denominator, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_minus, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder); static void divide_u16_without_allocation(UnsignedBigInteger const& numerator, UnsignedBigInteger::Word denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder); static void destructive_GCD_without_allocation(UnsignedBigInteger& temp_a, UnsignedBigInteger& temp_b, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_3, UnsignedBigInteger& temp_4, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_remainder, UnsignedBigInteger& output); - static void modular_inverse_without_allocation(UnsignedBigInteger const& a_, UnsignedBigInteger const& b, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_3, UnsignedBigInteger& temp_4, UnsignedBigInteger& temp_plus, UnsignedBigInteger& temp_minus, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_d, UnsignedBigInteger& temp_u, UnsignedBigInteger& temp_v, UnsignedBigInteger& temp_x, UnsignedBigInteger& result); + static void modular_inverse_without_allocation(UnsignedBigInteger const& a_, UnsignedBigInteger const& b, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_3, UnsignedBigInteger& temp_4, UnsignedBigInteger& temp_minus, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_d, UnsignedBigInteger& temp_u, UnsignedBigInteger& temp_v, UnsignedBigInteger& temp_x, UnsignedBigInteger& result); static void destructive_modular_power_without_allocation(UnsignedBigInteger& ep, UnsignedBigInteger& base, UnsignedBigInteger const& m, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_3, UnsignedBigInteger& temp_4, UnsignedBigInteger& temp_multiply, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_remainder, UnsignedBigInteger& result); private: diff --git a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index 15547aaaff..d99ab08176 100644 --- a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -144,6 +144,15 @@ void UnsignedBigInteger::clamp_to_trimmed_length() m_words.resize(length); } +void UnsignedBigInteger::resize_with_leading_zeros(size_t new_length) +{ + size_t old_length = length(); + if (old_length < new_length) { + m_words.resize_and_keep_capacity(new_length); + __builtin_memset(&m_words.data()[old_length], 0, (new_length - old_length) * sizeof(u32)); + } +} + FLATTEN UnsignedBigInteger UnsignedBigInteger::plus(const UnsignedBigInteger& other) const { UnsignedBigInteger result; @@ -215,9 +224,8 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::multiplied_by(const UnsignedBigIn UnsignedBigInteger temp_shift_result; UnsignedBigInteger temp_shift_plus; UnsignedBigInteger temp_shift; - UnsignedBigInteger temp_plus; - UnsignedBigIntegerAlgorithms::multiply_without_allocation(*this, other, temp_shift_result, temp_shift_plus, temp_shift, temp_plus, result); + UnsignedBigIntegerAlgorithms::multiply_without_allocation(*this, other, temp_shift_result, temp_shift_plus, temp_shift, result); return result; } diff --git a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index eb73686e5c..896952a4df 100644 --- a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -65,6 +65,7 @@ public: size_t trimmed_length() const; void clamp_to_trimmed_length(); + void resize_with_leading_zeros(size_t num_words); UnsignedBigInteger plus(const UnsignedBigInteger& other) const; UnsignedBigInteger minus(const UnsignedBigInteger& other) const; diff --git a/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp b/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp index f7df24bf84..83212d8b84 100644 --- a/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp +++ b/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp @@ -20,7 +20,6 @@ UnsignedBigInteger ModularInverse(const UnsignedBigInteger& a_, const UnsignedBi UnsignedBigInteger temp_2; UnsignedBigInteger temp_3; UnsignedBigInteger temp_4; - UnsignedBigInteger temp_plus; UnsignedBigInteger temp_minus; UnsignedBigInteger temp_quotient; UnsignedBigInteger temp_d; @@ -29,7 +28,7 @@ UnsignedBigInteger ModularInverse(const UnsignedBigInteger& a_, const UnsignedBi UnsignedBigInteger temp_x; UnsignedBigInteger result; - UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation(a_, b, temp_1, temp_2, temp_3, temp_4, temp_plus, temp_minus, temp_quotient, temp_d, temp_u, temp_v, temp_x, result); + UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation(a_, b, temp_1, temp_2, temp_3, temp_4, temp_minus, temp_quotient, temp_d, temp_u, temp_v, temp_x, result); return result; } @@ -93,7 +92,7 @@ UnsignedBigInteger LCM(const UnsignedBigInteger& a, const UnsignedBigInteger& b) // output = (a / gcd_output) * b UnsignedBigIntegerAlgorithms::divide_without_allocation(a, gcd_output, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); - UnsignedBigIntegerAlgorithms::multiply_without_allocation(temp_quotient, b, temp_1, temp_2, temp_3, temp_4, output); + UnsignedBigIntegerAlgorithms::multiply_without_allocation(temp_quotient, b, temp_1, temp_2, temp_3, output); dbgln_if(NT_DEBUG, "quot: {} rem: {} out: {}", temp_quotient, temp_remainder, output); diff --git a/Userland/Utilities/test-crypto.cpp b/Userland/Utilities/test-crypto.cpp index 349ffa44fc..42f954c1d4 100644 --- a/Userland/Utilities/test-crypto.cpp +++ b/Userland/Utilities/test-crypto.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -2244,6 +2245,94 @@ static void bigint_addition_edgecases() FAIL(Incorrect Result); } } + { + I_TEST((BigInteger | Basic add to accumulator)); + Crypto::UnsignedBigInteger num1(10); + Crypto::UnsignedBigInteger num2(70); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + if (num1.words() == Vector { 80 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Add to empty accumulator)); + Crypto::UnsignedBigInteger num1({}); + Crypto::UnsignedBigInteger num2(10); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + if (num1.words() == Vector { 10 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Add to smaller accumulator)); + Crypto::UnsignedBigInteger num1(10); + Crypto::UnsignedBigInteger num2({ 10, 10 }); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + if (num1.words() == Vector { 20, 10 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Add to accumulator with carry)); + Crypto::UnsignedBigInteger num1(UINT32_MAX - 1); + Crypto::UnsignedBigInteger num2(2); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + if (num1.words() == Vector { 0, 1 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Add to accumulator with multiple carries)); + Crypto::UnsignedBigInteger num1({ UINT32_MAX - 2, UINT32_MAX - 1 }); + Crypto::UnsignedBigInteger num2({ 5, 1 }); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + if (num1.words() == Vector { 2, 0, 1 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Add to accumulator with multiple carry levels)); + Crypto::UnsignedBigInteger num1({ UINT32_MAX - 2, UINT32_MAX }); + Crypto::UnsignedBigInteger num2(5); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + if (num1.words() == Vector { 2, 0, 1 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Add to accumulator with leading zero)); + Crypto::UnsignedBigInteger num1(1); + Crypto::UnsignedBigInteger num2({ 1, 0 }); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + if (num1.words() == Vector { 2 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Add to accumulator with carry and leading zero)); + Crypto::UnsignedBigInteger num1({ UINT32_MAX, 0, 0, 0 }); + Crypto::UnsignedBigInteger num2({ 1, 0 }); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + if (num1.words() == Vector { 0, 1, 0, 0 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } } static void bigint_subtraction()