diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index b4b73b6532..1406ca8917 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -69,6 +69,7 @@ file(GLOB LIBCOMPRESS_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCom file(GLOB LIBCOMPRESS_TESTS CONFIGURE_DEPENDS "../../Tests/LibCompress/*.cpp") file(GLOB LIBCRYPTO_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCrypto/*.cpp") file(GLOB LIBCRYPTO_SUBDIR_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCrypto/*/*.cpp") +file(GLOB LIBCRYPTO_SUBSUBDIR_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCrypto/*/*/*.cpp") file(GLOB LIBTLS_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTLS/*.cpp") file(GLOB LIBTTF_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTTF/*.cpp") file(GLOB LIBTEXTCODEC_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTextCodec/*.cpp") @@ -85,7 +86,7 @@ file(GLOB LIBTEST_MAIN CONFIGURE_DEPENDS "../../Userland/Libraries/LibTest/TestM set(LAGOM_REGEX_SOURCES ${LIBREGEX_LIBC_SOURCES} ${LIBREGEX_SOURCES}) set(LAGOM_CORE_SOURCES ${AK_SOURCES} ${LIBCORE_SOURCES}) -set(LAGOM_MORE_SOURCES ${LIBARCHIVE_SOURCES} ${LIBAUDIO_SOURCES} ${LIBELF_SOURCES} ${LIBIPC_SOURCES} ${LIBLINE_SOURCES} ${LIBJS_SOURCES} ${LIBJS_SUBDIR_SOURCES} ${LIBX86_SOURCES} ${LIBCRYPTO_SOURCES} ${LIBCOMPRESS_SOURCES} ${LIBCRYPTO_SUBDIR_SOURCES} ${LIBTLS_SOURCES} ${LIBTTF_SOURCES} ${LIBTEXTCODEC_SOURCES} ${LIBMARKDOWN_SOURCES} ${LIBGEMINI_SOURCES} ${LIBGFX_SOURCES} ${LIBGUI_GML_SOURCES} ${LIBHTTP_SOURCES} ${LAGOM_REGEX_SOURCES} ${SHELL_SOURCES} ${LIBSQL_SOURCES}) +set(LAGOM_MORE_SOURCES ${LIBARCHIVE_SOURCES} ${LIBAUDIO_SOURCES} ${LIBELF_SOURCES} ${LIBIPC_SOURCES} ${LIBLINE_SOURCES} ${LIBJS_SOURCES} ${LIBJS_SUBDIR_SOURCES} ${LIBX86_SOURCES} ${LIBCRYPTO_SOURCES} ${LIBCOMPRESS_SOURCES} ${LIBCRYPTO_SUBDIR_SOURCES} ${LIBCRYPTO_SUBSUBDIR_SOURCES} ${LIBTLS_SOURCES} ${LIBTTF_SOURCES} ${LIBTEXTCODEC_SOURCES} ${LIBMARKDOWN_SOURCES} ${LIBGEMINI_SOURCES} ${LIBGFX_SOURCES} ${LIBGUI_GML_SOURCES} ${LIBHTTP_SOURCES} ${LAGOM_REGEX_SOURCES} ${SHELL_SOURCES} ${LIBSQL_SOURCES}) set(LAGOM_TEST_SOURCES ${LIBTEST_SOURCES}) # FIXME: This is a hack, because the lagom stuff can be build individually or diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp new file mode 100644 index 0000000000..01675349e2 --- /dev/null +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2020, Itamar S. + * Copyright (c) 2020-2021, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "UnsignedBigIntegerAlgorithms.h" + +namespace Crypto { + +/** + * Complexity: O(N) where N is the number of words in the shorter value + * Method: + * Apply word-wise until words in the shorter value are used up + * then copy the rest of the words verbatim from the longer value. + */ +FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_or_without_allocation( + UnsignedBigInteger const& left, + UnsignedBigInteger const& right, + UnsignedBigInteger& output) +{ + // If either of the BigInts are invalid, the output is just the other one. + if (left.is_invalid()) { + output.set_to(right); + return; + } + if (right.is_invalid()) { + output.set_to(left); + return; + } + + const UnsignedBigInteger *shorter, *longer; + if (left.length() < right.length()) { + shorter = &left; + longer = &right; + } else { + shorter = &right; + longer = &left; + } + + output.m_words.resize_and_keep_capacity(longer->length()); + + size_t longer_offset = longer->length() - shorter->length(); + for (size_t i = 0; i < shorter->length(); ++i) + output.m_words[i] = longer->words()[i] | shorter->words()[i]; + + __builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset); +} + +/** + * Complexity: O(N) where N is the number of words in the shorter value + * Method: + * Apply 'and' word-wise until words in the shorter value are used up + * and zero the rest. + */ +FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_and_without_allocation( + UnsignedBigInteger const& left, + UnsignedBigInteger const& right, + UnsignedBigInteger& output) +{ + // If either of the BigInts are invalid, the output is just the other one. + if (left.is_invalid()) { + output.set_to(right); + return; + } + if (right.is_invalid()) { + output.set_to(left); + return; + } + + const UnsignedBigInteger *shorter, *longer; + if (left.length() < right.length()) { + shorter = &left; + longer = &right; + } else { + shorter = &right; + longer = &left; + } + + output.m_words.resize_and_keep_capacity(longer->length()); + + size_t longer_offset = longer->length() - shorter->length(); + for (size_t i = 0; i < shorter->length(); ++i) + output.m_words[i] = longer->words()[i] & shorter->words()[i]; + + __builtin_memset(output.m_words.data() + shorter->length(), 0, sizeof(u32) * longer_offset); +} + +/** + * Complexity: O(N) where N is the number of words in the shorter value + * Method: + * Apply 'xor' word-wise until words in the shorter value are used up + * and copy the rest. + */ +FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_xor_without_allocation( + UnsignedBigInteger const& left, + UnsignedBigInteger const& right, + UnsignedBigInteger& output) +{ + // If either of the BigInts are invalid, the output is just the other one. + if (left.is_invalid()) { + output.set_to(right); + return; + } + if (right.is_invalid()) { + output.set_to(left); + return; + } + + const UnsignedBigInteger *shorter, *longer; + if (left.length() < right.length()) { + shorter = &left; + longer = &right; + } else { + shorter = &right; + longer = &left; + } + + output.m_words.resize_and_keep_capacity(longer->length()); + + size_t longer_offset = longer->length() - shorter->length(); + for (size_t i = 0; i < shorter->length(); ++i) + output.m_words[i] = longer->words()[i] ^ shorter->words()[i]; + + __builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset); +} + +/** + * Complexity: O(N) where N is the number of words + */ +FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_not_without_allocation( + UnsignedBigInteger const& right, + UnsignedBigInteger& output) +{ + // If the value is invalid, the output value is invalid as well. + if (right.is_invalid()) { + output.invalidate(); + return; + } + if (right.length() == 0) { + output.set_to_0(); + return; + } + + output.m_words.resize_and_keep_capacity(right.length()); + + if (right.length() > 1) { + for (size_t i = 0; i < right.length() - 1; ++i) + output.m_words[i] = ~right.words()[i]; + } + + auto last_word_index = right.length() - 1; + auto last_word = right.words()[last_word_index]; + + output.m_words[last_word_index] = ((u32)0xffffffffffffffff >> __builtin_clz(last_word)) & ~last_word; +} + +/** + * Complexity : O(N + num_bits % 8) where N is the number of words in the number + * Shift method : + * Start by shifting by whole words in num_bits (by putting missing words at the start), + * then shift the number's words two by two by the remaining amount of bits. + */ +FLATTEN void UnsignedBigIntegerAlgorithms::shift_left_without_allocation( + UnsignedBigInteger const& number, + size_t num_bits, + UnsignedBigInteger& temp_result, + UnsignedBigInteger& temp_plus, + UnsignedBigInteger& output) +{ + // We can only do shift operations on individual words + // where the shift amount is <= size of word (32). + // But we do know how to shift by a multiple of word size (e.g 64=32*2) + // So we first shift the result by how many whole words fit in 'num_bits' + shift_left_by_n_words(number, num_bits / UnsignedBigInteger::BITS_IN_WORD, temp_result); + + output.set_to(temp_result); + + // And now we shift by the leftover amount of bits + num_bits %= UnsignedBigInteger::BITS_IN_WORD; + + if (num_bits == 0) { + return; + } + + for (size_t i = 0; i < temp_result.length(); ++i) { + u32 current_word_of_temp_result = shift_left_get_one_word(temp_result, num_bits, i); + output.m_words[i] = current_word_of_temp_result; + } + + // Shifting the last word can produce a carry + u32 carry_word = shift_left_get_one_word(temp_result, num_bits, temp_result.length()); + if (carry_word != 0) { + + // output += (carry_word << temp_result.length()) + // FIXME : Using temp_plus this way to transform carry_word into a bigint is not + // efficient nor pretty. Maybe we should have an "add_with_shift" method ? + 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); + } +} + +ALWAYS_INLINE void UnsignedBigIntegerAlgorithms::shift_left_by_n_words( + UnsignedBigInteger const& number, + size_t number_of_words, + UnsignedBigInteger& output) +{ + // shifting left by N words means just inserting N zeroes to the beginning of the words vector + output.set_to_0(); + output.m_words.resize_and_keep_capacity(number_of_words + number.length()); + + __builtin_memset(output.m_words.data(), 0, number_of_words * sizeof(unsigned)); + __builtin_memcpy(&output.m_words.data()[number_of_words], number.m_words.data(), number.m_words.size() * sizeof(unsigned)); +} + +/** + * Returns the word at a requested index in the result of a shift operation + */ +ALWAYS_INLINE u32 UnsignedBigIntegerAlgorithms::shift_left_get_one_word( + UnsignedBigInteger const& number, + size_t num_bits, + size_t result_word_index) +{ + // "<= length()" (rather than length() - 1) is intentional, + // The result inedx of length() is used when calculating the carry word + VERIFY(result_word_index <= number.length()); + VERIFY(num_bits <= UnsignedBigInteger::BITS_IN_WORD); + u32 result = 0; + + // we need to check for "num_bits != 0" since shifting right by 32 is apparently undefined behaviour! + if (result_word_index > 0 && num_bits != 0) { + result += number.m_words[result_word_index - 1] >> (UnsignedBigInteger::BITS_IN_WORD - num_bits); + } + if (result_word_index < number.length() && num_bits < 32) { + result += number.m_words[result_word_index] << num_bits; + } + return result; +} + +} diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/Division.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/Division.cpp new file mode 100644 index 0000000000..1607404db6 --- /dev/null +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/Division.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020, Itamar S. + * Copyright (c) 2020-2021, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "UnsignedBigIntegerAlgorithms.h" + +namespace Crypto { + +/** + * 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) { + size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index; + shift_left_without_allocation(denominator, shift_amount, temp_shift_result, temp_shift_plus, temp_shift); + + subtract_without_allocation(remainder, temp_shift, temp_minus); + if (!temp_minus.is_invalid()) { + remainder.set_to(temp_minus); + quotient.set_bit_inplace(shift_amount); + } + } + } +} + +/** + * Complexity : O(N) where N is the number of digits in the numerator + * Division method : + * Starting from the most significant one, for each half-word of the numerator, combine it + * with the existing remainder if any, divide the combined number as a u32 operation and + * update the quotient / remainder as needed. + */ +FLATTEN void UnsignedBigIntegerAlgorithms::divide_u16_without_allocation( + UnsignedBigInteger const& numerator, + u32 denominator, + UnsignedBigInteger& quotient, + UnsignedBigInteger& remainder) +{ + VERIFY(denominator < (1 << 16)); + u32 remainder_word = 0; + auto numerator_length = numerator.trimmed_length(); + quotient.set_to_0(); + quotient.m_words.resize(numerator_length); + for (int word_index = numerator_length - 1; word_index >= 0; --word_index) { + auto word_high = numerator.m_words[word_index] >> 16; + auto word_low = numerator.m_words[word_index] & ((1 << 16) - 1); + + auto number_to_divide_high = (remainder_word << 16) | word_high; + auto quotient_high = number_to_divide_high / denominator; + remainder_word = number_to_divide_high % denominator; + + auto number_to_divide_low = remainder_word << 16 | word_low; + auto quotient_low = number_to_divide_low / denominator; + remainder_word = number_to_divide_low % denominator; + + quotient.m_words[word_index] = (quotient_high << 16) | quotient_low; + } + remainder.set_to(remainder_word); +} + +} diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/GCD.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/GCD.cpp new file mode 100644 index 0000000000..8727bb141a --- /dev/null +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/GCD.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, Ali Mohammad Pur + * Copyright (c) 2020-2021, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "UnsignedBigIntegerAlgorithms.h" + +namespace Crypto { + +void UnsignedBigIntegerAlgorithms::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) +{ + for (;;) { + if (temp_a == 0) { + output.set_to(temp_b); + return; + } + + // temp_b %= temp_a + divide_without_allocation(temp_b, temp_a, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); + temp_b.set_to(temp_remainder); + if (temp_b == 0) { + output.set_to(temp_a); + return; + } + + // temp_a %= temp_b + divide_without_allocation(temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); + temp_a.set_to(temp_remainder); + } +} + +} diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp new file mode 100644 index 0000000000..49ddfbd623 --- /dev/null +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020, Ali Mohammad Pur + * Copyright (c) 2020-2021, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "UnsignedBigIntegerAlgorithms.h" + +namespace Crypto { + +void UnsignedBigIntegerAlgorithms::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) +{ + UnsignedBigInteger one { 1 }; + + 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); + } + + temp_v.set_to(b); + temp_x.set_to(0); + + // d = b - 1 + subtract_without_allocation(b, one, temp_d); + + while (!(temp_v == 1)) { + while (temp_v < temp_u) { + // u -= v + subtract_without_allocation(temp_u, temp_v, temp_minus); + temp_u.set_to(temp_minus); + + // d += x + add_without_allocation(temp_d, temp_x, temp_plus); + temp_d.set_to(temp_plus); + + 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); + } + + // u /= 2 + divide_u16_without_allocation(temp_u, 2, temp_quotient, temp_1); + temp_u.set_to(temp_quotient); + + // d /= 2 + divide_u16_without_allocation(temp_d, 2, temp_quotient, temp_1); + temp_d.set_to(temp_quotient); + } + } + + // v -= u + subtract_without_allocation(temp_v, temp_u, temp_minus); + temp_v.set_to(temp_minus); + + // x += d + add_without_allocation(temp_x, temp_d, temp_plus); + temp_x.set_to(temp_plus); + + 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); + } + + // v /= 2 + divide_u16_without_allocation(temp_v, 2, temp_quotient, temp_1); + temp_v.set_to(temp_quotient); + + // x /= 2 + divide_u16_without_allocation(temp_x, 2, temp_quotient, temp_1); + temp_x.set_to(temp_quotient); + } + } + + // return x % b + divide_without_allocation(temp_x, b, temp_1, temp_2, temp_3, temp_4, temp_quotient, result); +} + +} diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp new file mode 100644 index 0000000000..e6a1b44fcc --- /dev/null +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, Ali Mohammad Pur + * Copyright (c) 2020-2021, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "UnsignedBigIntegerAlgorithms.h" + +namespace Crypto { + +void UnsignedBigIntegerAlgorithms::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& exp) +{ + exp.set_to(1); + 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); + divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); + exp.set_to(temp_remainder); + } + + // ep = ep / 2; + divide_u16_without_allocation(ep, 2, temp_quotient, temp_remainder); + ep.set_to(temp_quotient); + + // base = (base * base) % m; + multiply_without_allocation(base, base, temp_1, temp_2, temp_3, temp_4, 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); + + // Note that not clamping here would cause future calculations (multiply, specifically) to allocate even more unused space + // which would then persist through the temp bigints, and significantly slow down later loops. + // To avoid that, we can clamp to a specific max size, or just clamp to the min needed amount of space. + ep.clamp_to_trimmed_length(); + exp.clamp_to_trimmed_length(); + base.clamp_to_trimmed_length(); + } +} + +} diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp b/Userland/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp new file mode 100644 index 0000000000..eece4430b1 --- /dev/null +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Itamar S. + * Copyright (c) 2020-2021, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "UnsignedBigIntegerAlgorithms.h" + +namespace Crypto { + +/** + * Complexity: O(N^2) where N is the number of words in the larger number + * Multiplication method: + * An integer is equal to the sum of the powers of two + * according to the indices of its 'on' bits. + * So to multiple x*y, we go over each '1' bit in x (say the i'th bit), + * and add y< + * Copyright (c) 2020-2021, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "UnsignedBigIntegerAlgorithms.h" + +namespace Crypto { + +/** + * Complexity: O(N) where N is the number of words in the larger number + */ +void UnsignedBigIntegerAlgorithms::add_without_allocation( + UnsignedBigInteger const& left, + UnsignedBigInteger const& right, + UnsignedBigInteger& output) +{ + const UnsignedBigInteger* const longer = (left.length() > right.length()) ? &left : &right; + const UnsignedBigInteger* const shorter = (longer == &right) ? &left : &right; + + u8 carry = 0; + + output.set_to_0(); + output.m_words.resize_and_keep_capacity(longer->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 (carry) { + word_addition_result++; + } + carry = carry_out; + output.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; + } + output.m_words[i] = word_addition_result; + } + if (carry) { + output.m_words.append(carry); + } +} + +/** + * Complexity: O(N) where N is the number of words in the larger number + */ +void UnsignedBigIntegerAlgorithms::subtract_without_allocation( + UnsignedBigInteger const& left, + UnsignedBigInteger const& right, + UnsignedBigInteger& output) +{ + if (left < right) { + output.invalidate(); + return; + } + + u8 borrow = 0; + auto own_length = left.length(); + auto other_length = right.length(); + + output.set_to_0(); + output.m_words.resize_and_keep_capacity(own_length); + + for (size_t i = 0; i < own_length; ++i) { + u32 other_word = (i < other_length) ? right.m_words[i] : 0; + i64 temp = static_cast(left.m_words[i]) - static_cast(other_word) - static_cast(borrow); + // If temp < 0, we had an underflow + borrow = (temp >= 0) ? 0 : 1; + if (temp < 0) { + temp += (UINT32_MAX + 1); + } + output.m_words[i] = temp; + } + + // This assertion should not fail, because we verified that *this>=other at the beginning of the function + VERIFY(borrow == 0); +} + +} diff --git a/Userland/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h b/Userland/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h new file mode 100644 index 0000000000..9f740aaebc --- /dev/null +++ b/Userland/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Crypto { + +class UnsignedBigIntegerAlgorithms { +public: + static void add_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output); + 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 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, u32 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 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: + ALWAYS_INLINE static void shift_left_by_n_words(UnsignedBigInteger const& number, size_t number_of_words, UnsignedBigInteger& output); + ALWAYS_INLINE static u32 shift_left_get_one_word(UnsignedBigInteger const& number, size_t num_bits, size_t result_word_index); +}; + +} diff --git a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index d762bcbe5d..79ef86c76a 100644 --- a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -6,6 +6,7 @@ #include "UnsignedBigInteger.h" #include +#include namespace Crypto { @@ -85,7 +86,7 @@ String UnsignedBigInteger::to_base10() const UnsignedBigInteger remainder; while (temp != UnsignedBigInteger { 0 }) { - divide_u16_without_allocation(temp, 10, quotient, remainder); + UnsignedBigIntegerAlgorithms::divide_u16_without_allocation(temp, 10, quotient, remainder); VERIFY(remainder.words()[0] < 10); builder.append(static_cast(remainder.words()[0] + '0')); temp.set_to(quotient); @@ -147,7 +148,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::plus(const UnsignedBigInteger& ot { UnsignedBigInteger result; - add_without_allocation(*this, other, result); + UnsignedBigIntegerAlgorithms::add_without_allocation(*this, other, result); return result; } @@ -156,7 +157,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::minus(const UnsignedBigInteger& o { UnsignedBigInteger result; - subtract_without_allocation(*this, other, result); + UnsignedBigIntegerAlgorithms::subtract_without_allocation(*this, other, result); return result; } @@ -165,7 +166,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_or(const UnsignedBigInteg { UnsignedBigInteger result; - bitwise_or_without_allocation(*this, other, result); + UnsignedBigIntegerAlgorithms::bitwise_or_without_allocation(*this, other, result); return result; } @@ -174,7 +175,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_and(const UnsignedBigInte { UnsignedBigInteger result; - bitwise_and_without_allocation(*this, other, result); + UnsignedBigIntegerAlgorithms::bitwise_and_without_allocation(*this, other, result); return result; } @@ -183,7 +184,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_xor(const UnsignedBigInte { UnsignedBigInteger result; - bitwise_xor_without_allocation(*this, other, result); + UnsignedBigIntegerAlgorithms::bitwise_xor_without_allocation(*this, other, result); return result; } @@ -192,7 +193,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_not() const { UnsignedBigInteger result; - bitwise_not_without_allocation(*this, result); + UnsignedBigIntegerAlgorithms::bitwise_not_without_allocation(*this, result); return result; } @@ -203,7 +204,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::shift_left(size_t num_bits) const UnsignedBigInteger temp_result; UnsignedBigInteger temp_plus; - shift_left_without_allocation(*this, num_bits, temp_result, temp_plus, output); + UnsignedBigIntegerAlgorithms::shift_left_without_allocation(*this, num_bits, temp_result, temp_plus, output); return output; } @@ -216,7 +217,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::multiplied_by(const UnsignedBigIn UnsignedBigInteger temp_shift; UnsignedBigInteger temp_plus; - 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, temp_plus, result); return result; } @@ -229,7 +230,7 @@ FLATTEN UnsignedDivisionResult UnsignedBigInteger::divided_by(const UnsignedBigI // If we actually have a u16-compatible divisor, short-circuit to the // less computationally-intensive "divide_u16_without_allocation" method. if (divisor.trimmed_length() == 1 && divisor.m_words[0] < (1 << 16)) { - divide_u16_without_allocation(*this, divisor.m_words[0], quotient, remainder); + UnsignedBigIntegerAlgorithms::divide_u16_without_allocation(*this, divisor.m_words[0], quotient, remainder); return UnsignedDivisionResult { quotient, remainder }; } @@ -238,7 +239,7 @@ FLATTEN UnsignedDivisionResult UnsignedBigInteger::divided_by(const UnsignedBigI UnsignedBigInteger temp_shift; UnsignedBigInteger temp_minus; - divide_without_allocation(*this, divisor, temp_shift_result, temp_shift_plus, temp_shift, temp_minus, quotient, remainder); + UnsignedBigIntegerAlgorithms::divide_without_allocation(*this, divisor, temp_shift_result, temp_shift_plus, temp_shift, temp_minus, quotient, remainder); return UnsignedDivisionResult { quotient, remainder }; } @@ -300,423 +301,6 @@ bool UnsignedBigInteger::operator<(const UnsignedBigInteger& other) const return false; } -/** - * Complexity: O(N) where N is the number of words in the larger number - */ -void UnsignedBigInteger::add_without_allocation( - const UnsignedBigInteger& left, - const UnsignedBigInteger& right, - UnsignedBigInteger& output) -{ - const UnsignedBigInteger* const longer = (left.length() > right.length()) ? &left : &right; - const UnsignedBigInteger* const shorter = (longer == &right) ? &left : &right; - - u8 carry = 0; - - output.set_to_0(); - output.m_words.resize_and_keep_capacity(longer->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 (carry) { - word_addition_result++; - } - carry = carry_out; - output.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; - } - output.m_words[i] = word_addition_result; - } - if (carry) { - output.m_words.append(carry); - } -} - -/** - * Complexity: O(N) where N is the number of words in the larger number - */ -void UnsignedBigInteger::subtract_without_allocation( - const UnsignedBigInteger& left, - const UnsignedBigInteger& right, - UnsignedBigInteger& output) -{ - if (left < right) { - output.invalidate(); - return; - } - - u8 borrow = 0; - auto own_length = left.length(); - auto other_length = right.length(); - - output.set_to_0(); - output.m_words.resize_and_keep_capacity(own_length); - - for (size_t i = 0; i < own_length; ++i) { - u32 other_word = (i < other_length) ? right.m_words[i] : 0; - i64 temp = static_cast(left.m_words[i]) - static_cast(other_word) - static_cast(borrow); - // If temp < 0, we had an underflow - borrow = (temp >= 0) ? 0 : 1; - if (temp < 0) { - temp += (UINT32_MAX + 1); - } - output.m_words[i] = temp; - } - - // This assertion should not fail, because we verified that *this>=other at the beginning of the function - VERIFY(borrow == 0); -} - -/** - * Complexity: O(N) where N is the number of words in the shorter value - * Method: - * Apply word-wise until words in the shorter value are used up - * then copy the rest of the words verbatim from the longer value. - */ -FLATTEN void UnsignedBigInteger::bitwise_or_without_allocation( - const UnsignedBigInteger& left, - const UnsignedBigInteger& right, - UnsignedBigInteger& output) -{ - // If either of the BigInts are invalid, the output is just the other one. - if (left.is_invalid()) { - output.set_to(right); - return; - } - if (right.is_invalid()) { - output.set_to(left); - return; - } - - const UnsignedBigInteger *shorter, *longer; - if (left.length() < right.length()) { - shorter = &left; - longer = &right; - } else { - shorter = &right; - longer = &left; - } - - output.m_words.resize_and_keep_capacity(longer->length()); - - size_t longer_offset = longer->length() - shorter->length(); - for (size_t i = 0; i < shorter->length(); ++i) - output.m_words[i] = longer->words()[i] | shorter->words()[i]; - - __builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset); -} - -/** - * Complexity: O(N) where N is the number of words in the shorter value - * Method: - * Apply 'and' word-wise until words in the shorter value are used up - * and zero the rest. - */ -FLATTEN void UnsignedBigInteger::bitwise_and_without_allocation( - const UnsignedBigInteger& left, - const UnsignedBigInteger& right, - UnsignedBigInteger& output) -{ - // If either of the BigInts are invalid, the output is just the other one. - if (left.is_invalid()) { - output.set_to(right); - return; - } - if (right.is_invalid()) { - output.set_to(left); - return; - } - - const UnsignedBigInteger *shorter, *longer; - if (left.length() < right.length()) { - shorter = &left; - longer = &right; - } else { - shorter = &right; - longer = &left; - } - - output.m_words.resize_and_keep_capacity(longer->length()); - - size_t longer_offset = longer->length() - shorter->length(); - for (size_t i = 0; i < shorter->length(); ++i) - output.m_words[i] = longer->words()[i] & shorter->words()[i]; - - __builtin_memset(output.m_words.data() + shorter->length(), 0, sizeof(u32) * longer_offset); -} - -/** - * Complexity: O(N) where N is the number of words in the shorter value - * Method: - * Apply 'xor' word-wise until words in the shorter value are used up - * and copy the rest. - */ -FLATTEN void UnsignedBigInteger::bitwise_xor_without_allocation( - const UnsignedBigInteger& left, - const UnsignedBigInteger& right, - UnsignedBigInteger& output) -{ - // If either of the BigInts are invalid, the output is just the other one. - if (left.is_invalid()) { - output.set_to(right); - return; - } - if (right.is_invalid()) { - output.set_to(left); - return; - } - - const UnsignedBigInteger *shorter, *longer; - if (left.length() < right.length()) { - shorter = &left; - longer = &right; - } else { - shorter = &right; - longer = &left; - } - - output.m_words.resize_and_keep_capacity(longer->length()); - - size_t longer_offset = longer->length() - shorter->length(); - for (size_t i = 0; i < shorter->length(); ++i) - output.m_words[i] = longer->words()[i] ^ shorter->words()[i]; - - __builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset); -} - -/** - * Complexity: O(N) where N is the number of words - */ -FLATTEN void UnsignedBigInteger::bitwise_not_without_allocation( - const UnsignedBigInteger& right, - UnsignedBigInteger& output) -{ - // If the value is invalid, the output value is invalid as well. - if (right.is_invalid()) { - output.invalidate(); - return; - } - if (right.length() == 0) { - output.set_to_0(); - return; - } - - output.m_words.resize_and_keep_capacity(right.length()); - - if (right.length() > 1) { - for (size_t i = 0; i < right.length() - 1; ++i) - output.m_words[i] = ~right.words()[i]; - } - - auto last_word_index = right.length() - 1; - auto last_word = right.words()[last_word_index]; - - output.m_words[last_word_index] = ((u32)0xffffffffffffffff >> __builtin_clz(last_word)) & ~last_word; -} - -/** - * Complexity : O(N + num_bits % 8) where N is the number of words in the number - * Shift method : - * Start by shifting by whole words in num_bits (by putting missing words at the start), - * then shift the number's words two by two by the remaining amount of bits. - */ -FLATTEN void UnsignedBigInteger::shift_left_without_allocation( - const UnsignedBigInteger& number, - size_t num_bits, - UnsignedBigInteger& temp_result, - UnsignedBigInteger& temp_plus, - UnsignedBigInteger& output) -{ - // We can only do shift operations on individual words - // where the shift amount is <= size of word (32). - // But we do know how to shift by a multiple of word size (e.g 64=32*2) - // So we first shift the result by how many whole words fit in 'num_bits' - shift_left_by_n_words(number, num_bits / UnsignedBigInteger::BITS_IN_WORD, temp_result); - - output.set_to(temp_result); - - // And now we shift by the leftover amount of bits - num_bits %= UnsignedBigInteger::BITS_IN_WORD; - - if (num_bits == 0) { - return; - } - - for (size_t i = 0; i < temp_result.length(); ++i) { - u32 current_word_of_temp_result = shift_left_get_one_word(temp_result, num_bits, i); - output.m_words[i] = current_word_of_temp_result; - } - - // Shifting the last word can produce a carry - u32 carry_word = shift_left_get_one_word(temp_result, num_bits, temp_result.length()); - if (carry_word != 0) { - - // output += (carry_word << temp_result.length()) - // FIXME : Using temp_plus this way to transform carry_word into a bigint is not - // efficient nor pretty. Maybe we should have an "add_with_shift" method ? - 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); - } -} - -/** - * Complexity: O(N^2) where N is the number of words in the larger number - * Multiplication method: - * An integer is equal to the sum of the powers of two - * according to the indices of its 'on' bits. - * So to multiple x*y, we go over each '1' bit in x (say the i'th bit), - * and add y<= 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; - shift_left_without_allocation(denominator, shift_amount, temp_shift_result, temp_shift_plus, temp_shift); - - subtract_without_allocation(remainder, temp_shift, temp_minus); - if (!temp_minus.is_invalid()) { - remainder.set_to(temp_minus); - quotient.set_bit_inplace(shift_amount); - } - } - } -} - -/** - * Complexity : O(N) where N is the number of digits in the numerator - * Division method : - * Starting from the most significant one, for each half-word of the numerator, combine it - * with the existing remainder if any, divide the combined number as a u32 operation and - * update the quotient / remainder as needed. - */ -FLATTEN void UnsignedBigInteger::divide_u16_without_allocation( - const UnsignedBigInteger& numerator, - u32 denominator, - UnsignedBigInteger& quotient, - UnsignedBigInteger& remainder) -{ - VERIFY(denominator < (1 << 16)); - u32 remainder_word = 0; - auto numerator_length = numerator.trimmed_length(); - quotient.set_to_0(); - quotient.m_words.resize(numerator_length); - for (int word_index = numerator_length - 1; word_index >= 0; --word_index) { - auto word_high = numerator.m_words[word_index] >> 16; - auto word_low = numerator.m_words[word_index] & ((1 << 16) - 1); - - auto number_to_divide_high = (remainder_word << 16) | word_high; - auto quotient_high = number_to_divide_high / denominator; - remainder_word = number_to_divide_high % denominator; - - auto number_to_divide_low = remainder_word << 16 | word_low; - auto quotient_low = number_to_divide_low / denominator; - remainder_word = number_to_divide_low % denominator; - - quotient.m_words[word_index] = (quotient_high << 16) | quotient_low; - } - remainder.set_to(remainder_word); -} - -ALWAYS_INLINE void UnsignedBigInteger::shift_left_by_n_words( - const UnsignedBigInteger& number, - const size_t number_of_words, - UnsignedBigInteger& output) -{ - // shifting left by N words means just inserting N zeroes to the beginning of the words vector - output.set_to_0(); - output.m_words.resize_and_keep_capacity(number_of_words + number.length()); - - __builtin_memset(output.m_words.data(), 0, number_of_words * sizeof(unsigned)); - __builtin_memcpy(&output.m_words.data()[number_of_words], number.m_words.data(), number.m_words.size() * sizeof(unsigned)); -} - -/** - * Returns the word at a requested index in the result of a shift operation - */ -ALWAYS_INLINE u32 UnsignedBigInteger::shift_left_get_one_word( - const UnsignedBigInteger& number, - const size_t num_bits, - const size_t result_word_index) -{ - // "<= length()" (rather than length() - 1) is intentional, - // The result inedx of length() is used when calculating the carry word - VERIFY(result_word_index <= number.length()); - VERIFY(num_bits <= UnsignedBigInteger::BITS_IN_WORD); - u32 result = 0; - - // we need to check for "num_bits != 0" since shifting right by 32 is apparently undefined behaviour! - if (result_word_index > 0 && num_bits != 0) { - result += number.m_words[result_word_index - 1] >> (UnsignedBigInteger::BITS_IN_WORD - num_bits); - } - if (result_word_index < number.length() && num_bits < 32) { - result += number.m_words[result_word_index] << num_bits; - } - return result; -} } void AK::Formatter::format(FormatBuilder& fmtbuilder, const Crypto::UnsignedBigInteger& value) diff --git a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index 9af596b4da..e2d467fc42 100644 --- a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -75,24 +75,12 @@ public: void set_bit_inplace(size_t bit_index); - static void add_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output); - static void subtract_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output); - static void bitwise_or_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output); - static void bitwise_and_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output); - static void bitwise_xor_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output); - static void bitwise_not_without_allocation(const UnsignedBigInteger& left, UnsignedBigInteger& output); - static void shift_left_without_allocation(const UnsignedBigInteger& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output); - static void multiply_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output); - static void divide_without_allocation(const UnsignedBigInteger& numerator, const UnsignedBigInteger& 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(const UnsignedBigInteger& numerator, u32 denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder); - bool operator==(const UnsignedBigInteger& other) const; bool operator!=(const UnsignedBigInteger& other) const; bool operator<(const UnsignedBigInteger& other) const; private: - ALWAYS_INLINE static void shift_left_by_n_words(const UnsignedBigInteger& number, size_t number_of_words, UnsignedBigInteger& output); - ALWAYS_INLINE static u32 shift_left_get_one_word(const UnsignedBigInteger& number, size_t num_bits, size_t result_word_index); + friend class UnsignedBigIntegerAlgorithms; static constexpr size_t BITS_IN_WORD = 32; // Little endian diff --git a/Userland/Libraries/LibCrypto/CMakeLists.txt b/Userland/Libraries/LibCrypto/CMakeLists.txt index f7654894dc..626b961185 100644 --- a/Userland/Libraries/LibCrypto/CMakeLists.txt +++ b/Userland/Libraries/LibCrypto/CMakeLists.txt @@ -5,6 +5,13 @@ set(SOURCES ASN1/DER.cpp ASN1/PEM.cpp Authentication/GHash.cpp + BigInt/Algorithms/BitwiseOperations.cpp + BigInt/Algorithms/Division.cpp + BigInt/Algorithms/GCD.cpp + BigInt/Algorithms/ModularInverse.cpp + BigInt/Algorithms/ModularPower.cpp + BigInt/Algorithms/Multiplication.cpp + BigInt/Algorithms/SimpleOperations.cpp BigInt/SignedBigInteger.cpp BigInt/UnsignedBigInteger.cpp Checksum/Adler32.cpp diff --git a/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp b/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp index e24700c07a..f7df24bf84 100644 --- a/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp +++ b/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp @@ -5,6 +5,7 @@ */ #include +#include #include namespace Crypto { @@ -15,7 +16,6 @@ UnsignedBigInteger ModularInverse(const UnsignedBigInteger& a_, const UnsignedBi if (b == 1) return { 1 }; - UnsignedBigInteger one { 1 }; UnsignedBigInteger temp_1; UnsignedBigInteger temp_2; UnsignedBigInteger temp_3; @@ -23,78 +23,14 @@ UnsignedBigInteger ModularInverse(const UnsignedBigInteger& a_, const UnsignedBi UnsignedBigInteger temp_plus; UnsignedBigInteger temp_minus; UnsignedBigInteger temp_quotient; - UnsignedBigInteger temp_remainder; - UnsignedBigInteger d; + UnsignedBigInteger temp_d; + UnsignedBigInteger temp_u; + UnsignedBigInteger temp_v; + UnsignedBigInteger temp_x; + UnsignedBigInteger result; - auto a = a_; - auto u = a; - if (a.words()[0] % 2 == 0) { - // u += b - UnsignedBigInteger::add_without_allocation(u, b, temp_plus); - u.set_to(temp_plus); - } - - auto v = b; - UnsignedBigInteger x { 0 }; - - // d = b - 1 - UnsignedBigInteger::subtract_without_allocation(b, one, d); - - while (!(v == 1)) { - while (v < u) { - // u -= v - UnsignedBigInteger::subtract_without_allocation(u, v, temp_minus); - u.set_to(temp_minus); - - // d += x - UnsignedBigInteger::add_without_allocation(d, x, temp_plus); - d.set_to(temp_plus); - - while (u.words()[0] % 2 == 0) { - if (d.words()[0] % 2 == 1) { - // d += b - UnsignedBigInteger::add_without_allocation(d, b, temp_plus); - d.set_to(temp_plus); - } - - // u /= 2 - UnsignedBigInteger::divide_u16_without_allocation(u, 2, temp_quotient, temp_remainder); - u.set_to(temp_quotient); - - // d /= 2 - UnsignedBigInteger::divide_u16_without_allocation(d, 2, temp_quotient, temp_remainder); - d.set_to(temp_quotient); - } - } - - // v -= u - UnsignedBigInteger::subtract_without_allocation(v, u, temp_minus); - v.set_to(temp_minus); - - // x += d - UnsignedBigInteger::add_without_allocation(x, d, temp_plus); - x.set_to(temp_plus); - - while (v.words()[0] % 2 == 0) { - if (x.words()[0] % 2 == 1) { - // x += b - UnsignedBigInteger::add_without_allocation(x, b, temp_plus); - x.set_to(temp_plus); - } - - // v /= 2 - UnsignedBigInteger::divide_u16_without_allocation(v, 2, temp_quotient, temp_remainder); - v.set_to(temp_quotient); - - // x /= 2 - UnsignedBigInteger::divide_u16_without_allocation(x, 2, temp_quotient, temp_remainder); - x.set_to(temp_quotient); - } - } - - // x % b - UnsignedBigInteger::divide_without_allocation(x, b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); - return temp_remainder; + 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); + return result; } UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigInteger& e, const UnsignedBigInteger& m) @@ -104,8 +40,8 @@ UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigIn UnsignedBigInteger ep { e }; UnsignedBigInteger base { b }; - UnsignedBigInteger exp { 1 }; + UnsignedBigInteger result; UnsignedBigInteger temp_1; UnsignedBigInteger temp_2; UnsignedBigInteger temp_3; @@ -114,72 +50,15 @@ UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigIn UnsignedBigInteger temp_quotient; UnsignedBigInteger temp_remainder; - while (!(ep < 1)) { - if (ep.words()[0] % 2 == 1) { - // exp = (exp * base) % m; - UnsignedBigInteger::multiply_without_allocation(exp, base, temp_1, temp_2, temp_3, temp_4, temp_multiply); - UnsignedBigInteger::divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); - exp.set_to(temp_remainder); - } + UnsignedBigIntegerAlgorithms::destructive_modular_power_without_allocation(ep, base, m, temp_1, temp_2, temp_3, temp_4, temp_multiply, temp_quotient, temp_remainder, result); - // ep = ep / 2; - UnsignedBigInteger::divide_u16_without_allocation(ep, 2, temp_quotient, temp_remainder); - ep.set_to(temp_quotient); - - // base = (base * base) % m; - UnsignedBigInteger::multiply_without_allocation(base, base, temp_1, temp_2, temp_3, temp_4, temp_multiply); - UnsignedBigInteger::divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); - base.set_to(temp_remainder); - - // Note that not clamping here would cause future calculations (multiply, specifically) to allocate even more unused space - // which would then persist through the temp bigints, and significantly slow down later loops. - // To avoid that, we can clamp to a specific max size, or just clamp to the min needed amount of space. - ep.clamp_to_trimmed_length(); - exp.clamp_to_trimmed_length(); - base.clamp_to_trimmed_length(); - } - return exp; -} - -static void GCD_without_allocation( - const UnsignedBigInteger& a, - const UnsignedBigInteger& b, - 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) -{ - temp_a.set_to(a); - temp_b.set_to(b); - for (;;) { - if (temp_a == 0) { - output.set_to(temp_b); - return; - } - - // temp_b %= temp_a - UnsignedBigInteger::divide_without_allocation(temp_b, temp_a, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); - temp_b.set_to(temp_remainder); - if (temp_b == 0) { - output.set_to(temp_a); - return; - } - - // temp_a %= temp_b - UnsignedBigInteger::divide_without_allocation(temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); - temp_a.set_to(temp_remainder); - } + return result; } UnsignedBigInteger GCD(const UnsignedBigInteger& a, const UnsignedBigInteger& b) { - UnsignedBigInteger temp_a; - UnsignedBigInteger temp_b; + UnsignedBigInteger temp_a { a }; + UnsignedBigInteger temp_b { b }; UnsignedBigInteger temp_1; UnsignedBigInteger temp_2; UnsignedBigInteger temp_3; @@ -188,15 +67,15 @@ UnsignedBigInteger GCD(const UnsignedBigInteger& a, const UnsignedBigInteger& b) UnsignedBigInteger temp_remainder; UnsignedBigInteger output; - GCD_without_allocation(a, b, temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, output); + UnsignedBigIntegerAlgorithms::destructive_GCD_without_allocation(temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, output); return output; } UnsignedBigInteger LCM(const UnsignedBigInteger& a, const UnsignedBigInteger& b) { - UnsignedBigInteger temp_a; - UnsignedBigInteger temp_b; + UnsignedBigInteger temp_a { a }; + UnsignedBigInteger temp_b { b }; UnsignedBigInteger temp_1; UnsignedBigInteger temp_2; UnsignedBigInteger temp_3; @@ -206,15 +85,15 @@ UnsignedBigInteger LCM(const UnsignedBigInteger& a, const UnsignedBigInteger& b) UnsignedBigInteger gcd_output; UnsignedBigInteger output { 0 }; - GCD_without_allocation(a, b, temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, gcd_output); + UnsignedBigIntegerAlgorithms::destructive_GCD_without_allocation(temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, gcd_output); if (gcd_output == 0) { dbgln_if(NT_DEBUG, "GCD is zero"); return output; } // output = (a / gcd_output) * b - UnsignedBigInteger::divide_without_allocation(a, gcd_output, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); - UnsignedBigInteger::multiply_without_allocation(temp_quotient, b, temp_1, temp_2, temp_3, temp_4, output); + 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); dbgln_if(NT_DEBUG, "quot: {} rem: {} out: {}", temp_quotient, temp_remainder, output);