diff --git a/Libraries/LibCrypto/ASN1/DER.cpp b/Libraries/LibCrypto/ASN1/DER.cpp index 3d4489fedac..f0a9d6d959f 100644 --- a/Libraries/LibCrypto/ASN1/DER.cpp +++ b/Libraries/LibCrypto/ASN1/DER.cpp @@ -302,7 +302,7 @@ ErrorOr Encoder::write_arbitrary_sized_integer(UnsignedBigInteger const& v auto kind = kind_override.value_or(Kind::Integer); TRY(write_tag(class_, type, kind)); - auto max_byte_size = max(1ull, value.length() * UnsignedBigInteger::BITS_IN_WORD / 8); // At minimum, we need one byte to encode 0. + auto max_byte_size = max(1ull, value.byte_length()); // At minimum, we need one byte to encode 0. ByteBuffer buffer; auto output = TRY(buffer.get_bytes_for_writing(max_byte_size)); auto size = value.export_data(output); diff --git a/Libraries/LibCrypto/BigFraction/BigFraction.cpp b/Libraries/LibCrypto/BigFraction/BigFraction.cpp index 69b32094c90..4e7cb05269f 100644 --- a/Libraries/LibCrypto/BigFraction/BigFraction.cpp +++ b/Libraries/LibCrypto/BigFraction/BigFraction.cpp @@ -157,14 +157,14 @@ double BigFraction::to_double() const // NOTE: the precision of the result will be 63 bits (more than 53 bits necessary for the mantissa of a double). if (top_bit_numerator < (top_bit_denominator + 64)) { shift_left_numerator = top_bit_denominator + 64 - top_bit_numerator; - numerator = numerator.shift_left(shift_left_numerator); // copy + numerator = MUST(numerator.shift_left(shift_left_numerator)); // copy } // NOTE: Do nothing if numerator already has more than 64 bits more than denominator. // 2. Divide [potentially shifted] numerator by the denominator. auto division_result = numerator.divided_by(denominator); if (!division_result.remainder.is_zero()) { - division_result.quotient = division_result.quotient.shift_left(1).plus(1); // Extend the quotient with a "fake 1". + division_result.quotient = MUST(division_result.quotient.shift_left(1)).plus(1); // Extend the quotient with a "fake 1". // NOTE: Since the quotient has at least 63 bits, this will only affect the mantissa // on rounding, and have the same effect on rounding as any fractional digits (from the remainder). shift_left_numerator++; diff --git a/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp b/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp deleted file mode 100644 index 7801816c023..00000000000 --- a/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2020-2021, Dex♪ - * Copyright (c) 2025, Manuel Zahariev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "UnsignedBigIntegerAlgorithms.h" -#include -#include - -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) -{ - UnsignedBigInteger const *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) -{ - UnsignedBigInteger const *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) -{ - UnsignedBigInteger const *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 ErrorOr UnsignedBigIntegerAlgorithms::bitwise_not_fill_to_one_based_index_without_allocation( - UnsignedBigInteger const& right, - size_t index, - UnsignedBigInteger& output) -{ - if (index == 0) { - output.set_to_0(); - return {}; - } - size_t size = (index + UnsignedBigInteger::BITS_IN_WORD - 1) / UnsignedBigInteger::BITS_IN_WORD; - - TRY(output.m_words.try_resize_and_keep_capacity(size)); - VERIFY(size > 0); - for (size_t i = 0; i < size - 1; ++i) - output.m_words[i] = ~(i < right.length() ? right.words()[i] : 0); - - index -= (size - 1) * UnsignedBigInteger::BITS_IN_WORD; - auto last_word_index = size - 1; - auto last_word = last_word_index < right.length() ? right.words()[last_word_index] : 0; - - output.m_words[last_word_index] = (NumericLimits::max() >> (UnsignedBigInteger::BITS_IN_WORD - index)) & ~last_word; - - return {}; -} - -FLATTEN void UnsignedBigIntegerAlgorithms::shift_left_without_allocation( - UnsignedBigInteger const& number, - size_t num_bits, - UnsignedBigInteger& output) -{ - MUST(try_shift_left_without_allocation(number, num_bits, output)); -} - -/** - * Complexity : O(N) where N is the number of words in the number - */ -FLATTEN ErrorOr UnsignedBigIntegerAlgorithms::try_shift_left_without_allocation( - UnsignedBigInteger const& number, - size_t num_bits, - UnsignedBigInteger& output) -{ - size_t const bit_shift = num_bits % UnsignedBigInteger::BITS_IN_WORD; - size_t const bit_shift_complement = UnsignedBigInteger::BITS_IN_WORD - bit_shift; - - size_t const zero_based_index_of_highest_set_bit_in_hiword = (number.one_based_index_of_highest_set_bit() - 1) % UnsignedBigInteger::BITS_IN_WORD; - - // true if the high word is a result of the bit_shift - bool const hiword_shift = (bit_shift + zero_based_index_of_highest_set_bit_in_hiword) >= UnsignedBigInteger::BITS_IN_WORD; - size_t const word_shift = num_bits / UnsignedBigInteger::BITS_IN_WORD; - - TRY(try_shift_left_by_n_words(number, word_shift + (hiword_shift ? 1 : 0), output)); - - if (bit_shift == 0) // shifting left by an exact number of words) - return {}; - - UnsignedBigInteger::Word carry = 0; - for (size_t i = 0; i < number.length(); ++i) { - size_t const output_index = i + word_shift; - - output.m_words[output_index] = (number.m_words.at(i) << bit_shift) | carry; - carry = (number.m_words.at(i) >> bit_shift_complement); - } - - if (hiword_shift) - output.m_words[output.length() - 1] = carry; - - return {}; -} - -/** - * Complexity : O(N) where N is the number of words in the number - */ -FLATTEN void UnsignedBigIntegerAlgorithms::shift_right_without_allocation( - UnsignedBigInteger const& number, - size_t num_bits, - UnsignedBigInteger& output) -{ - size_t const bit_shift = num_bits % UnsignedBigInteger::BITS_IN_WORD; - size_t const bit_shift_complement = UnsignedBigInteger::BITS_IN_WORD - bit_shift; - size_t const zero_based_index_of_highest_set_bit_in_hiword = (number.one_based_index_of_highest_set_bit() - 1) % UnsignedBigInteger::BITS_IN_WORD; - - // true if the high word will be zeroed as a result of the shift - bool const hiword_zero = (bit_shift > zero_based_index_of_highest_set_bit_in_hiword); - size_t const word_shift = num_bits / UnsignedBigInteger::BITS_IN_WORD + (hiword_zero ? 1 : 0); - - if (word_shift >= number.length()) { // all non-zero digits have been shifted right; result is zero - output.set_to_0(); - return; - } - - shift_right_by_n_words(number, word_shift, output); - - if (bit_shift == 0) // shifting right by an exact number of words) - return; - - size_t const output_length = output.length(); - size_t number_index = number.length() - 1; - - UnsignedBigInteger::Word carry = 0; - - if (hiword_zero) { - carry = number.words().at(number_index) << bit_shift_complement; - --number_index; - } - - for (size_t i = 0; i < output_length; ++i) { - size_t const output_index = output_length - i - 1; // downto index 0 - - output.m_words[output_index] = ((number.m_words.at(number_index) >> bit_shift)) | carry; - carry = (number.m_words.at(number_index) << bit_shift_complement); - --number_index; - } -} - -void UnsignedBigIntegerAlgorithms::shift_left_by_n_words( - UnsignedBigInteger const& number, - size_t number_of_words, - UnsignedBigInteger& output) -{ - MUST(try_shift_left_by_n_words(number, number_of_words, output)); -} - -ErrorOr UnsignedBigIntegerAlgorithms::try_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(); - TRY(output.m_words.try_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)); - - return {}; -} - -void UnsignedBigIntegerAlgorithms::shift_right_by_n_words( - UnsignedBigInteger const& number, - size_t number_of_words, - UnsignedBigInteger& output) -{ - // shifting right by N words means just not copying the first words - output.set_to_0(); - output.m_words.resize_and_keep_capacity(number.length() - number_of_words); - __builtin_memcpy(output.m_words.data(), &number.m_words.data()[number_of_words], (number.m_words.size() - number_of_words) * sizeof(unsigned)); -} - -/** - * Returns the word at a requested index in the result of a shift operation - */ -ALWAYS_INLINE UnsignedBigInteger::Word 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 index 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 behavior! - 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/Libraries/LibCrypto/BigInt/Algorithms/Division.cpp b/Libraries/LibCrypto/BigInt/Algorithms/Division.cpp deleted file mode 100644 index cbdb1c6f47a..00000000000 --- a/Libraries/LibCrypto/BigInt/Algorithms/Division.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. - * Copyright (c) 2020-2021, Dex♪ - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "UnsignedBigIntegerAlgorithms.h" -#include - -namespace Crypto { - -using AK::Detail::div_mod_words; -using AK::Detail::dword; - -/** - * Complexity: O(N^2) where N is the number of words in the larger number - * Division method: - * Knuth's Algorithm D, see UFixedBigIntDivision.h for more details - */ -FLATTEN void UnsignedBigIntegerAlgorithms::divide_without_allocation( - UnsignedBigInteger const& numerator, - UnsignedBigInteger const& denominator, - UnsignedBigInteger& quotient, - UnsignedBigInteger& remainder) -{ - size_t dividend_len = numerator.trimmed_length(); - size_t divisor_len = denominator.trimmed_length(); - - VERIFY(divisor_len != 0); - - // Fast paths - // Division by 1 - if (divisor_len == 1 && denominator.m_words[0] == 1) { - quotient.set_to(numerator); - remainder.set_to_0(); - return; - } - - if (dividend_len < divisor_len) { - quotient.set_to_0(); - remainder.set_to(numerator); - return; - } - - if (divisor_len == 1 && dividend_len == 1) { - quotient.set_to(numerator.m_words[0] / denominator.m_words[0]); - remainder.set_to(numerator.m_words[0] % denominator.m_words[0]); - return; - } - // Division by Word - if (divisor_len == 1) { - quotient.resize_with_leading_zeros(dividend_len); - remainder.resize_with_leading_zeros(1); - - // FIXME: Use a "DoubleWord" to allow increasing the Word size of - // BigInt in the future - static_assert(UnsignedBigInteger::BITS_IN_WORD == 32); - auto u = dword(numerator.m_words[dividend_len - 2], numerator.m_words[dividend_len - 1]); - auto divisor = denominator.m_words[0]; - - auto top = u / divisor; - quotient.m_words[dividend_len - 1] = top >> UnsignedBigInteger::BITS_IN_WORD; - quotient.m_words[dividend_len - 2] = static_cast(top); - - auto carry = static_cast(u % divisor); - for (size_t i = dividend_len - 2; i-- != 0;) - quotient.m_words[i] = div_mod_words(numerator.m_words[i], carry, divisor, carry); - remainder.m_words[0] = carry; - return; - } - - // Knuth's algorithm D - auto dividend = numerator; - dividend.resize_with_leading_zeros(dividend_len + 1); - auto divisor = denominator; - - quotient.resize_with_leading_zeros(dividend_len - divisor_len + 1); - remainder.resize_with_leading_zeros(divisor_len); - - Ops::div_mod_internal( - dividend.words_span(), divisor.words_span(), - quotient.words_span(), remainder.words_span(), - dividend_len, divisor_len); -} - -/** - * 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, - UnsignedBigInteger::Word denominator, - UnsignedBigInteger& quotient, - UnsignedBigInteger& remainder) -{ - VERIFY(denominator < (1 << 16)); - UnsignedBigInteger::Word 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/Libraries/LibCrypto/BigInt/Algorithms/GCD.cpp b/Libraries/LibCrypto/BigInt/Algorithms/GCD.cpp deleted file mode 100644 index cce14b63834..00000000000 --- a/Libraries/LibCrypto/BigInt/Algorithms/GCD.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * Copyright (c) 2020-2021, Dex♪ - * Copyright (c) 2024, Altomani Gianluca - * - * 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_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_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_quotient, temp_remainder); - temp_a.set_to(temp_remainder); - } -} - -void UnsignedBigIntegerAlgorithms::extended_GCD_without_allocation( - UnsignedBigInteger const& a, - UnsignedBigInteger const& b, - UnsignedBigInteger& x, - UnsignedBigInteger& y, - UnsignedBigInteger& gcd, - UnsignedBigInteger& temp_quotient, - UnsignedBigInteger& temp_1, - UnsignedBigInteger& temp_2, - UnsignedBigInteger& temp_shift, - UnsignedBigInteger& temp_r, - UnsignedBigInteger& temp_s, - UnsignedBigInteger& temp_t) -{ - gcd.set_to(a); - x.set_to(1); - y.set_to(0); - - temp_r.set_to(b); - temp_s.set_to_0(); - temp_t.set_to(1); - - while (temp_r != 0) { - // quotient := old_r div r - divide_without_allocation(gcd, temp_r, temp_quotient, temp_1); - - temp_2.set_to(temp_r); - multiply_without_allocation(temp_quotient, temp_r, temp_shift, temp_1); - while (gcd < temp_1) { - add_into_accumulator_without_allocation(gcd, b); - } - MUST(subtract_without_allocation(gcd, temp_1, temp_r)); - gcd.set_to(temp_2); - - // (old_s, s) := (s, old_s − quotient × s) - temp_2.set_to(temp_s); - multiply_without_allocation(temp_quotient, temp_s, temp_shift, temp_1); - while (x < temp_1) { - add_into_accumulator_without_allocation(x, b); - } - MUST(subtract_without_allocation(x, temp_1, temp_s)); - x.set_to(temp_2); - - // (old_t, t) := (t, old_t − quotient × t) - temp_2.set_to(temp_t); - multiply_without_allocation(temp_quotient, temp_t, temp_shift, temp_1); - while (y < temp_1) { - add_into_accumulator_without_allocation(y, b); - } - MUST(subtract_without_allocation(y, temp_1, temp_t)); - y.set_to(temp_2); - } -} - -} diff --git a/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp b/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp deleted file mode 100644 index 6f1d9bcdb34..00000000000 --- a/Libraries/LibCrypto/BigInt/Algorithms/ModularInverse.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * Copyright (c) 2020-2021, Dex♪ - * Copyright (c) 2024, Altomani Gianluca - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "UnsignedBigIntegerAlgorithms.h" - -namespace Crypto { - -void UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation( - UnsignedBigInteger const& a, - UnsignedBigInteger const& b, - UnsignedBigInteger& result, - UnsignedBigInteger& temp_y, - UnsignedBigInteger& temp_gcd, - UnsignedBigInteger& temp_quotient, - UnsignedBigInteger& temp_1, - UnsignedBigInteger& temp_2, - UnsignedBigInteger& temp_shift, - UnsignedBigInteger& temp_r, - UnsignedBigInteger& temp_s, - UnsignedBigInteger& temp_t) -{ - extended_GCD_without_allocation(a, b, result, temp_y, temp_gcd, temp_quotient, temp_1, temp_2, temp_shift, temp_r, temp_s, temp_t); - - divide_without_allocation(result, b, temp_quotient, temp_1); - add_into_accumulator_without_allocation(temp_1, b); - divide_without_allocation(temp_1, b, temp_quotient, result); -} - -} diff --git a/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp b/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp deleted file mode 100644 index 08001c8749e..00000000000 --- a/Libraries/LibCrypto/BigInt/Algorithms/ModularPower.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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_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_multiply); - divide_without_allocation(temp_multiply, m, temp_quotient, temp_remainder); - exp.set_to(temp_remainder); - } - - // ep = ep / 2; - ep.set_to(ep.shift_right(1)); - - // base = (base * base) % m; - multiply_without_allocation(base, base, temp_1, temp_multiply); - divide_without_allocation(temp_multiply, m, 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(); - } -} - -/** - * Compute (1/value) % 2^32. - * This needs an odd input value - * Algorithm from: Dumas, J.G. "On Newton–Raphson Iteration for Multiplicative Inverses Modulo Prime Powers". - */ -ALWAYS_INLINE static u32 inverse_wrapped(u32 value) -{ - VERIFY(value & 1); - - u64 b = static_cast(value); - u64 k0 = (2 - b); - u64 t = (b - 1); - size_t i = 1; - while (i < 32) { - t = t * t; - k0 = k0 * (t + 1); - i <<= 1; - } - return static_cast(-k0); -} - -/** - * Computes z = x * y + c. z_carry contains the top bits, z contains the bottom bits. - */ -ALWAYS_INLINE static void linear_multiplication_with_carry(u32 x, u32 y, u32 c, u32& z_carry, u32& z) -{ - u64 result = static_cast(x) * static_cast(y) + static_cast(c); - z_carry = static_cast(result >> 32); - z = static_cast(result); -} - -/** - * Computes z = a + b. z_carry contains the top bit (1 or 0), z contains the bottom bits. - */ -ALWAYS_INLINE static void addition_with_carry(u32 a, u32 b, u32& z_carry, u32& z) -{ - u64 result = static_cast(a) + static_cast(b); - z_carry = static_cast(result >> 32); - z = static_cast(result); -} - -/** - * Computes a montgomery "fragment" for y_i. This computes "z[i] += x[i] * y_i" for all words while rippling the carry, and returns the carry. - * Algorithm from: Gueron, "Efficient Software Implementations of Modular Exponentiation". (https://eprint.iacr.org/2011/239.pdf) - */ -UnsignedBigInteger::Word UnsignedBigIntegerAlgorithms::montgomery_fragment(UnsignedBigInteger& z, size_t offset_in_z, UnsignedBigInteger const& x, UnsignedBigInteger::Word y_digit, size_t num_words) -{ - UnsignedBigInteger::Word carry { 0 }; - for (size_t i = 0; i < num_words; ++i) { - UnsignedBigInteger::Word a_carry; - UnsignedBigInteger::Word a; - linear_multiplication_with_carry(x.m_words[i], y_digit, z.m_words[offset_in_z + i], a_carry, a); - UnsignedBigInteger::Word b_carry; - UnsignedBigInteger::Word b; - addition_with_carry(a, carry, b_carry, b); - z.m_words[offset_in_z + i] = b; - carry = a_carry + b_carry; - } - return carry; -} - -/** - * Computes the "almost montgomery" product : x * y * 2 ^ (-num_words * BITS_IN_WORD) % modulo - * [Note : that means that the result z satisfies z * 2^(num_words * BITS_IN_WORD) % modulo = x * y % modulo] - * assuming : - * - x, y and modulo are all already padded to num_words - * - k = inverse_wrapped(modulo) (optimization to not recompute K each time) - * Algorithm from: Gueron, "Efficient Software Implementations of Modular Exponentiation". (https://eprint.iacr.org/2011/239.pdf) - */ -void UnsignedBigIntegerAlgorithms::almost_montgomery_multiplication_without_allocation( - UnsignedBigInteger const& x, - UnsignedBigInteger const& y, - UnsignedBigInteger const& modulo, - UnsignedBigInteger& z, - UnsignedBigInteger::Word k, - size_t num_words, - UnsignedBigInteger& result) -{ - VERIFY(x.length() >= num_words); - VERIFY(y.length() >= num_words); - VERIFY(modulo.length() >= num_words); - - z.set_to(0); - z.resize_with_leading_zeros(num_words * 2); - - UnsignedBigInteger::Word previous_double_carry { 0 }; - for (size_t i = 0; i < num_words; ++i) { - // z[i->num_words+i] += x * y_i - UnsignedBigInteger::Word carry_1 = montgomery_fragment(z, i, x, y.m_words[i], num_words); - // z[i->num_words+i] += modulo * (z_i * k) - UnsignedBigInteger::Word t = z.m_words[i] * k; - UnsignedBigInteger::Word carry_2 = montgomery_fragment(z, i, modulo, t, num_words); - - // Compute the carry by combining all of the carries of the previous computations - // Put it "right after" the range that we computed above - UnsignedBigInteger::Word temp_carry = previous_double_carry + carry_1; - UnsignedBigInteger::Word overall_carry = temp_carry + carry_2; - z.m_words[num_words + i] = overall_carry; - - // Detect if there was a "double carry" for this word by checking if our carry results are smaller than their components - previous_double_carry = (temp_carry < carry_1 || overall_carry < carry_2) ? 1 : 0; - } - - if (previous_double_carry == 0) { - // Return the top num_words bytes of Z, which contains our result. - shift_right_by_n_words(z, num_words, result); - result.resize_with_leading_zeros(num_words); - return; - } - - // We have a carry, so we're "one bigger" than we need to be. - // Subtract the modulo from the result (the top half of z), and write it to the bottom half of Z since we have space. - // (With carry, of course.) - UnsignedBigInteger::Word c { 0 }; - for (size_t i = 0; i < num_words; ++i) { - UnsignedBigInteger::Word z_digit = z.m_words[num_words + i]; - UnsignedBigInteger::Word modulo_digit = modulo.m_words[i]; - UnsignedBigInteger::Word new_z_digit = z_digit - modulo_digit - c; - z.m_words[i] = new_z_digit; - // Detect if the subtraction underflowed - from "Hacker's Delight" - c = ((modulo_digit & ~z_digit) | ((modulo_digit | ~z_digit) & new_z_digit)) >> (UnsignedBigInteger::BITS_IN_WORD - 1); - } - - // Return the bottom num_words bytes of Z (with the carry bit handled) - z.m_words.resize(num_words); - result.set_to(z); - result.resize_with_leading_zeros(num_words); -} - -/** - * Complexity: still O(N^3) with N the number of words in the largest word, but less complex than the classical mod power. - * Note: the montgomery multiplications requires an inverse modulo over 2^32, which is only defined for odd numbers. - */ -void UnsignedBigIntegerAlgorithms::montgomery_modular_power_with_minimal_allocations( - UnsignedBigInteger const& base, - UnsignedBigInteger const& exponent, - UnsignedBigInteger const& modulo, - UnsignedBigInteger& temp_z, - UnsignedBigInteger& rr, - UnsignedBigInteger& one, - UnsignedBigInteger& z, - UnsignedBigInteger& zz, - UnsignedBigInteger& x, - UnsignedBigInteger& temp_extra, - UnsignedBigInteger& result) -{ - VERIFY(modulo.is_odd()); - - // Note: While this is a constexpr variable for clarity and could be changed in theory, - // various optimized parts of the algorithm rely on this value being exactly 4. - constexpr size_t window_size = 4; - - size_t num_words = modulo.trimmed_length(); - UnsignedBigInteger::Word k = inverse_wrapped(modulo.m_words[0]); - - one.set_to(1); - - // rr = ( 2 ^ (2 * modulo.length() * BITS_IN_WORD) ) % modulo - shift_left_by_n_words(one, 2 * num_words, x); - divide_without_allocation(x, modulo, temp_extra, rr); - rr.resize_with_leading_zeros(num_words); - - // x = base [% modulo, if x doesn't already fit in modulo's words] - x.set_to(base); - if (x.trimmed_length() > num_words) - divide_without_allocation(base, modulo, temp_extra, x); - x.resize_with_leading_zeros(num_words); - - one.set_to(1); - one.resize_with_leading_zeros(num_words); - - // Compute the montgomery powers from 0 to 2^window_size. powers[i] = x^i - UnsignedBigInteger powers[1 << window_size]; - almost_montgomery_multiplication_without_allocation(one, rr, modulo, temp_z, k, num_words, powers[0]); - almost_montgomery_multiplication_without_allocation(x, rr, modulo, temp_z, k, num_words, powers[1]); - for (size_t i = 2; i < (1 << window_size); ++i) - almost_montgomery_multiplication_without_allocation(powers[i - 1], powers[1], modulo, temp_z, k, num_words, powers[i]); - - z.set_to(powers[0]); - z.resize_with_leading_zeros(num_words); - zz.set_to(0); - zz.resize_with_leading_zeros(num_words); - - ssize_t exponent_length = exponent.trimmed_length(); - for (ssize_t word_in_exponent = exponent_length - 1; word_in_exponent >= 0; --word_in_exponent) { - UnsignedBigInteger::Word exponent_word = exponent.m_words[word_in_exponent]; - size_t bit_in_word = 0; - while (bit_in_word < UnsignedBigInteger::BITS_IN_WORD) { - if (word_in_exponent != exponent_length - 1 || bit_in_word != 0) { - almost_montgomery_multiplication_without_allocation(z, z, modulo, temp_z, k, num_words, zz); - almost_montgomery_multiplication_without_allocation(zz, zz, modulo, temp_z, k, num_words, z); - almost_montgomery_multiplication_without_allocation(z, z, modulo, temp_z, k, num_words, zz); - almost_montgomery_multiplication_without_allocation(zz, zz, modulo, temp_z, k, num_words, z); - } - auto power_index = exponent_word >> (UnsignedBigInteger::BITS_IN_WORD - window_size); - auto& power = powers[power_index]; - almost_montgomery_multiplication_without_allocation(z, power, modulo, temp_z, k, num_words, zz); - - swap(z, zz); - - // Move to the next window - exponent_word <<= window_size; - bit_in_word += window_size; - } - } - - almost_montgomery_multiplication_without_allocation(z, one, modulo, temp_z, k, num_words, zz); - - if (zz < modulo) { - result.set_to(zz); - result.clamp_to_trimmed_length(); - return; - } - - // Note : Since we were using "almost montgomery" multiplications, we aren't guaranteed to be under the modulo already. - // So, if we're here, we need to respect the modulo. - // We can, however, start by trying to subtract the modulo, just in case we're close. - MUST(subtract_without_allocation(zz, modulo, result)); - - if (modulo < zz) { - // Note: This branch shouldn't happen in theory (as noted in https://github.com/rust-num/num-bigint/blob/master/src/biguint/monty.rs#L210) - // Let's dbgln the values we used. That way, if we hit this branch, we can contribute these values for test cases. - dbgln("Encountered the modulo branch during a montgomery modular power. Params : {} - {} - {}", base, exponent, modulo); - // We just clobber all the other temporaries that we don't need for the division. - // This is wasteful, but we're on the edgiest of cases already. - divide_without_allocation(zz, modulo, temp_extra, result); - } - - result.clamp_to_trimmed_length(); -} - -} diff --git a/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp b/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp deleted file mode 100644 index 6e60bcdbc92..00000000000 --- a/Libraries/LibCrypto/BigInt/Algorithms/Multiplication.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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) -{ - UnsignedBigInteger const* const longer = (left.length() > right.length()) ? &left : &right; - UnsignedBigInteger const* const shorter = (longer == &right) ? &left : &right; - - output.set_to(*longer); - add_into_accumulator_without_allocation(output, *shorter); -} - -/** - * 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(); - - // 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; - } - 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; - } - word_addition_result += last_carry_for_word; - last_carry_for_word = current_carry_for_word; - accumulator.m_words[i] = word_addition_result; - } - - // 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; - } - accumulator.m_words[value_length] += last_carry_for_word; - last_carry_for_word = current_carry_for_word; - value_length++; - } - - 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); - } - - accumulator.m_cached_trimmed_length = {}; -} - -/** - * Complexity: O(N) where N is the number of words in the larger number - */ -ErrorOr UnsignedBigIntegerAlgorithms::subtract_without_allocation( - UnsignedBigInteger const& left, - UnsignedBigInteger const& right, - UnsignedBigInteger& output) -{ - if (left < right) { - return Error::from_string_literal("Invalid subtraction: left < right"); - } - - 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); - - return {}; -} - -} diff --git a/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h b/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h deleted file mode 100644 index 37b041cf049..00000000000 --- a/Libraries/LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021, Dex♪ - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Crypto { - -class UnsignedBigIntegerAlgorithms { - using Ops = AK::StorageOperations; - -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 ErrorOr 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 shift_left_without_allocation(UnsignedBigInteger const& number, size_t bits_to_shift_by, UnsignedBigInteger& output); - static void shift_right_without_allocation(UnsignedBigInteger const& number, size_t num_bits, UnsignedBigInteger& output); - static void multiply_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& temp_shift, UnsignedBigInteger& output); - static void divide_without_allocation(UnsignedBigInteger const& numerator, UnsignedBigInteger const& denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder); - static void divide_u16_without_allocation(UnsignedBigInteger const& numerator, UnsignedBigInteger::Word denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder); - - static ErrorOr bitwise_not_fill_to_one_based_index_without_allocation(UnsignedBigInteger const& left, size_t, UnsignedBigInteger& output); - static ErrorOr try_shift_left_without_allocation(UnsignedBigInteger const& number, size_t bits_to_shift_by, UnsignedBigInteger& output); - - static void extended_GCD_without_allocation(UnsignedBigInteger const& a, UnsignedBigInteger const& b, UnsignedBigInteger& x, UnsignedBigInteger& y, UnsignedBigInteger& gcd, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_r, UnsignedBigInteger& temp_s, UnsignedBigInteger& temp_t); - - static void destructive_GCD_without_allocation(UnsignedBigInteger& temp_a, UnsignedBigInteger& temp_b, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_remainder, UnsignedBigInteger& output); - static void modular_inverse_without_allocation(UnsignedBigInteger const& a, UnsignedBigInteger const& b, UnsignedBigInteger& result, UnsignedBigInteger& temp_y, UnsignedBigInteger& temp_gcd, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_r, UnsignedBigInteger& temp_s, UnsignedBigInteger& temp_t); - static void destructive_modular_power_without_allocation(UnsignedBigInteger& ep, UnsignedBigInteger& base, UnsignedBigInteger const& m, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_multiply, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_remainder, UnsignedBigInteger& result); - static void montgomery_modular_power_with_minimal_allocations(UnsignedBigInteger const& base, UnsignedBigInteger const& exponent, UnsignedBigInteger const& modulo, UnsignedBigInteger& temp_z0, UnsignedBigInteger& temp_rr, UnsignedBigInteger& temp_one, UnsignedBigInteger& temp_z, UnsignedBigInteger& temp_zz, UnsignedBigInteger& temp_x, UnsignedBigInteger& temp_extra, UnsignedBigInteger& result); - -private: - static UnsignedBigInteger::Word montgomery_fragment(UnsignedBigInteger& z, size_t offset_in_z, UnsignedBigInteger const& x, UnsignedBigInteger::Word y_digit, size_t num_words); - static void almost_montgomery_multiplication_without_allocation(UnsignedBigInteger const& x, UnsignedBigInteger const& y, UnsignedBigInteger const& modulo, UnsignedBigInteger& z, UnsignedBigInteger::Word k, size_t num_words, UnsignedBigInteger& result); - static void shift_left_by_n_words(UnsignedBigInteger const& number, size_t number_of_words, UnsignedBigInteger& output); - static void shift_right_by_n_words(UnsignedBigInteger const& number, size_t number_of_words, UnsignedBigInteger& output); - ALWAYS_INLINE static UnsignedBigInteger::Word shift_left_get_one_word(UnsignedBigInteger const& number, size_t num_bits, size_t result_word_index); - - static ErrorOr try_shift_left_by_n_words(UnsignedBigInteger const& number, size_t number_of_words, UnsignedBigInteger& output); -}; - -} diff --git a/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp b/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp index 589cfa9de8b..ed71326c839 100644 --- a/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp +++ b/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp @@ -1,243 +1,241 @@ /* * Copyright (c) 2020, the SerenityOS developers. * Copyright (c) 2022, David Tuin + * Copyright (c) 2025, Altomani Gianluca * * SPDX-License-Identifier: BSD-2-Clause */ -#include "SignedBigInteger.h" -#include #include +#include + +#include +#include +#include namespace Crypto { +SignedBigInteger::SignedBigInteger(UnsignedBigInteger&& unsigned_data, bool sign) +{ + MP_MUST(mp_init_copy(&m_mp, &unsigned_data.m_mp)); + if (sign) { + MP_MUST(mp_neg(&m_mp, &m_mp)); + } +} + +SignedBigInteger::SignedBigInteger(u8 const* ptr, size_t length) +{ + MP_MUST(mp_init(&m_mp)); + MP_MUST(mp_from_sbin(&m_mp, ptr, length)); +} + +SignedBigInteger::SignedBigInteger(UnsignedBigInteger const& unsigned_data) +{ + MP_MUST(mp_init_copy(&m_mp, &unsigned_data.m_mp)); +} + SignedBigInteger::SignedBigInteger(double value) - : m_sign(value < 0.0) - , m_unsigned_data(fabs(value)) { + MP_MUST(mp_init(&m_mp)); + MP_MUST(mp_set_double(&m_mp, value)); } -SignedBigInteger SignedBigInteger::import_data(u8 const* ptr, size_t length) +SignedBigInteger::SignedBigInteger(i64 value) { - bool sign = *ptr; - auto unsigned_data = UnsignedBigInteger::import_data(ptr + 1, length - 1); - return { move(unsigned_data), sign }; + MP_MUST(mp_init(&m_mp)); + mp_set_i64(&m_mp, value); } -size_t SignedBigInteger::export_data(Bytes data, bool remove_leading_zeros) const +SignedBigInteger::SignedBigInteger(SignedBigInteger const& other) { - // FIXME: Support this: - // m <0XX> -> m (if remove_leading_zeros) - VERIFY(!remove_leading_zeros); + MP_MUST(mp_init_copy(&m_mp, &other.m_mp)); +} - data[0] = m_sign; - auto bytes_view = data.slice(1, data.size() - 1); - return m_unsigned_data.export_data(bytes_view, remove_leading_zeros) + 1; +SignedBigInteger& SignedBigInteger::operator=(SignedBigInteger const& other) +{ + if (this == &other) + return *this; + mp_clear(&m_mp); + MP_MUST(mp_init_copy(&m_mp, &other.m_mp)); + return *this; +} + +SignedBigInteger::SignedBigInteger() +{ + MP_MUST(mp_init(&m_mp)); +} + +SignedBigInteger::~SignedBigInteger() +{ + mp_clear(&m_mp); +} + +size_t SignedBigInteger::export_data(Bytes data) const +{ + size_t written = 0; + MP_MUST(mp_to_sbin(&m_mp, data.data(), data.size(), &written)); + return written; } ErrorOr SignedBigInteger::from_base(u16 N, StringView str) { - auto sign = false; - if (str.length() > 1) { - auto maybe_sign = str[0]; - if (maybe_sign == '-') { - str = str.substring_view(1); - sign = true; + VERIFY(N <= 36); + if (str.is_empty()) + return SignedBigInteger(0); + + auto buffer = TRY(ByteBuffer::create_zeroed(str.length() + 1)); + + size_t idx = 0; + for (auto& c : str) { + if (c == '_') { + // Skip underscores + continue; } - if (maybe_sign == '+') - str = str.substring_view(1); + + buffer[idx++] = c; } - auto unsigned_data = TRY(UnsignedBigInteger::from_base(N, str)); - return SignedBigInteger { move(unsigned_data), sign }; + + SignedBigInteger result; + if (mp_read_radix(&result.m_mp, reinterpret_cast(buffer.data()), N) != MP_OKAY) + return Error::from_string_literal("Invalid number"); + return result; } ErrorOr SignedBigInteger::to_base(u16 N) const { - StringBuilder builder; + VERIFY(N <= 36); + if (is_zero()) + return "0"_string; - if (m_sign) - TRY(builder.try_append('-')); + int size = 0; + MP_MUST(mp_radix_size(&m_mp, N, &size)); + auto buffer = TRY(ByteBuffer::create_zeroed(size)); - auto unsigned_as_base = TRY(m_unsigned_data.to_base(N)); - TRY(builder.try_append(unsigned_as_base.bytes_as_string_view())); + size_t written = 0; + MP_MUST(mp_to_radix(&m_mp, reinterpret_cast(buffer.data()), size, &written, N)); - return builder.to_string(); + return StringView(buffer.bytes().slice(0, written - 1)).to_ascii_lowercase_string(); } u64 SignedBigInteger::to_u64() const { - u64 unsigned_value = m_unsigned_data.to_u64(); - if (!m_sign) - return unsigned_value; - return ~(unsigned_value - 1); // equivalent to `-unsigned_value`, but doesn't trigger UBSAN + return mp_get_u64(&m_mp); } double SignedBigInteger::to_double(UnsignedBigInteger::RoundingMode rounding_mode) const { - double unsigned_value = m_unsigned_data.to_double(rounding_mode); - if (!m_sign) - return unsigned_value; + int sign = mp_isneg(&m_mp) ? -1 : 1; + return unsigned_value().to_double(rounding_mode) * sign; +} - VERIFY(!is_zero()); - return -unsigned_value; +UnsignedBigInteger SignedBigInteger::unsigned_value() const +{ + UnsignedBigInteger result; + MP_MUST(mp_abs(&m_mp, &result.m_mp)); + return result; +} + +bool SignedBigInteger::is_negative() const +{ + return mp_isneg(&m_mp); +} + +bool SignedBigInteger::is_zero() const +{ + return mp_iszero(&m_mp); +} + +void SignedBigInteger::negate() +{ + MP_MUST(mp_neg(&m_mp, &m_mp)); + m_hash = {}; +} + +void SignedBigInteger::set_to_0() +{ + mp_zero(&m_mp); + m_hash = {}; +} + +void SignedBigInteger::set_to(i64 other) +{ + mp_set_i64(&m_mp, other); + m_hash = {}; +} + +void SignedBigInteger::set_to(SignedBigInteger const& other) +{ + MP_MUST(mp_copy(&other.m_mp, &m_mp)); + m_hash = {}; +} + +size_t SignedBigInteger::byte_length() const +{ + return mp_sbin_size(&m_mp); } FLATTEN SignedBigInteger SignedBigInteger::plus(SignedBigInteger const& other) const { - // If both are of the same sign, just add the unsigned data and return. - if (m_sign == other.m_sign) - return { other.m_unsigned_data.plus(m_unsigned_data), m_sign }; - - // One value is signed while the other is not. - return m_sign ? other.minus(this->m_unsigned_data) : minus(other.m_unsigned_data); + SignedBigInteger result; + MP_MUST(mp_add(&m_mp, &other.m_mp, &result.m_mp)); + return result; } FLATTEN SignedBigInteger SignedBigInteger::minus(SignedBigInteger const& other) const { - // If the signs are different, convert the op to an addition. - if (m_sign != other.m_sign) { - // -x - y = - (x + y) - // x - -y = (x + y) - SignedBigInteger result { other.m_unsigned_data.plus(this->m_unsigned_data) }; - if (m_sign) - result.negate(); - return result; - } - - if (!m_sign) { - // Both operands are positive. - // x - y = - (y - x) - if (m_unsigned_data < other.m_unsigned_data) { - // The result will be negative. - return { MUST(other.m_unsigned_data.minus(m_unsigned_data)), true }; - } - - // The result will be either zero, or positive. - return SignedBigInteger { MUST(m_unsigned_data.minus(other.m_unsigned_data)) }; - } - - // Both operands are negative. - // -x - -y = y - x - if (m_unsigned_data < other.m_unsigned_data) { - // The result will be positive. - return SignedBigInteger { MUST(other.m_unsigned_data.minus(m_unsigned_data)) }; - } - // y - x = - (x - y) - if (m_unsigned_data > other.m_unsigned_data) { - // The result will be negative. - return SignedBigInteger { MUST(m_unsigned_data.minus(other.m_unsigned_data)), true }; - } - // Both operands have the same magnitude, the result is positive zero. - return SignedBigInteger { 0 }; + SignedBigInteger result; + MP_MUST(mp_sub(&m_mp, &other.m_mp, &result.m_mp)); + return result; } FLATTEN SignedBigInteger SignedBigInteger::plus(UnsignedBigInteger const& other) const { - if (m_sign) { - if (other < m_unsigned_data) - return { MUST(m_unsigned_data.minus(other)), true }; - - return { MUST(other.minus(m_unsigned_data)), false }; - } - - return { m_unsigned_data.plus(other), false }; + SignedBigInteger result; + MP_MUST(mp_add(&m_mp, &other.m_mp, &result.m_mp)); + return result; } FLATTEN SignedBigInteger SignedBigInteger::minus(UnsignedBigInteger const& other) const { - if (m_sign) - return { m_unsigned_data.plus(m_unsigned_data), true }; - - if (other < m_unsigned_data) - return { MUST(m_unsigned_data.minus(other)), false }; - - return { MUST(other.minus(m_unsigned_data)), true }; + SignedBigInteger result; + MP_MUST(mp_sub(&m_mp, &other.m_mp, &result.m_mp)); + return result; } FLATTEN SignedBigInteger SignedBigInteger::bitwise_not() const { - // Bitwise operators assume two's complement, while SignedBigInteger uses sign-magnitude. - // In two's complement, -x := ~x + 1. - // Hence, ~x == -x -1 == -(x + 1). - SignedBigInteger result = plus(SignedBigInteger { 1 }); - result.negate(); + SignedBigInteger result; + MP_MUST(mp_complement(&m_mp, &result.m_mp)); return result; } FLATTEN SignedBigInteger SignedBigInteger::multiplied_by(UnsignedBigInteger const& other) const { - return { unsigned_value().multiplied_by(other), m_sign }; + SignedBigInteger result; + MP_MUST(mp_mul(&m_mp, &other.m_mp, &result.m_mp)); + return result; } FLATTEN SignedDivisionResult SignedBigInteger::divided_by(UnsignedBigInteger const& divisor) const { - auto division_result = unsigned_value().divided_by(divisor); - return { - { move(division_result.quotient), m_sign }, - { move(division_result.remainder), m_sign }, - }; + SignedBigInteger quotient; + SignedBigInteger remainder; + MP_MUST(mp_div(&m_mp, &divisor.m_mp, "ient.m_mp, &remainder.m_mp)); + return SignedDivisionResult { quotient, remainder }; } FLATTEN SignedBigInteger SignedBigInteger::bitwise_or(SignedBigInteger const& other) const { - // See bitwise_and() for derivations. - if (!is_negative() && !other.is_negative()) - return { unsigned_value().bitwise_or(other.unsigned_value()), false }; - - // -A | B == (~A + 1) | B == ~(A - 1) | B. The result is negative, so need to two's complement at the end to move the sign into the m_sign field. - // That can be simplified to: - // -(-A | B) == ~(~(A - 1) | B) + 1 = (A - 1) & ~B + 1 - // This saves one ~. - if (is_negative() && !other.is_negative()) { - size_t index = unsigned_value().one_based_index_of_highest_set_bit(); - return { MUST(unsigned_value().minus(1)).bitwise_and(other.unsigned_value().bitwise_not_fill_to_one_based_index(index)).plus(1), true }; - } - - // -(A | -B) == ~A & (B - 1) + 1 - if (!is_negative() && other.is_negative()) { - size_t index = other.unsigned_value().one_based_index_of_highest_set_bit(); - return { unsigned_value().bitwise_not_fill_to_one_based_index(index).bitwise_and(MUST(other.unsigned_value().minus(1))).plus(1), true }; - } - - return { MUST(unsigned_value().minus(1)).bitwise_and(MUST(other.unsigned_value().minus(1))).plus(1), true }; + SignedBigInteger result; + MP_MUST(mp_or(&m_mp, &other.m_mp, &result.m_mp)); + return result; } FLATTEN SignedBigInteger SignedBigInteger::bitwise_and(SignedBigInteger const& other) const { - if (!is_negative() && !other.is_negative()) - return { unsigned_value().bitwise_and(other.unsigned_value()), false }; - - // These two just use that -x == ~x + 1 (see below). - - // -A & B == (~A + 1) & B. - if (is_negative() && !other.is_negative()) { - size_t index = other.unsigned_value().one_based_index_of_highest_set_bit(); - return { unsigned_value().bitwise_not_fill_to_one_based_index(index).plus(1).bitwise_and(other.unsigned_value()), false }; - } - - // A & -B == A & (~B + 1). - if (!is_negative() && other.is_negative()) { - size_t index = unsigned_value().one_based_index_of_highest_set_bit(); - return { unsigned_value().bitwise_and(other.unsigned_value().bitwise_not_fill_to_one_based_index(index).plus(1)), false }; - } - - // Both numbers are negative. - // x + ~x == 0xff...ff, up to however many bits x is wide. - // In two's complement, x + ~x + 1 == 0 since the 1 in the overflowing bit position is masked out. - // Rearranging terms, ~x = -x - 1 (eq1). - // Substituting x = y - 1, ~(y - 1) == -(y - 1) - 1 == -y +1 -1 == -y, or ~(y - 1) == -y (eq2). - // Since both numbers are negative, we want to compute -A & -B. - // Per (eq2): - // -A & -B == ~(A - 1) & ~(B - 1) - // Inverting both sides: - // ~(-A & -B) == ~(~(A - 1) & ~(B - 1)) == ~~(A - 1) | ~~(B - 1) == (A - 1) | (B - 1). - // Applying (q1) on the LHS: - // -(-A & -B) - 1 == (A - 1) | (B - 1) - // Adding 1 on both sides and then multiplying both sides by -1: - // -A & -B == -( (A - 1) | (B - 1) + 1) - // So we can compute the bitwise and by returning a negative number with magnitude (A - 1) | (B - 1) + 1. - // This is better than the naive (~A + 1) & (~B + 1) because it needs just one O(n) scan for the or instead of 2 for the ~s. - return { MUST(unsigned_value().minus(1)).bitwise_or(MUST(other.unsigned_value().minus(1))).plus(1), true }; + SignedBigInteger result; + MP_MUST(mp_and(&m_mp, &other.m_mp, &result.m_mp)); + return result; } FLATTEN SignedBigInteger SignedBigInteger::bitwise_xor(SignedBigInteger const& other) const @@ -247,23 +245,17 @@ FLATTEN SignedBigInteger SignedBigInteger::bitwise_xor(SignedBigInteger const& o bool SignedBigInteger::operator==(UnsignedBigInteger const& other) const { - if (m_sign && m_unsigned_data != 0) - return false; - return m_unsigned_data == other; + return mp_cmp(&m_mp, &other.m_mp) == MP_EQ; } bool SignedBigInteger::operator!=(UnsignedBigInteger const& other) const { - if (m_sign) - return true; - return m_unsigned_data != other; + return !(*this == other); } bool SignedBigInteger::operator<(UnsignedBigInteger const& other) const { - if (m_sign) - return true; - return m_unsigned_data < other; + return mp_cmp(&m_mp, &other.m_mp) == MP_LT; } bool SignedBigInteger::operator>(UnsignedBigInteger const& other) const @@ -271,67 +263,65 @@ bool SignedBigInteger::operator>(UnsignedBigInteger const& other) const return *this != other && !(*this < other); } -FLATTEN ErrorOr SignedBigInteger::try_shift_left(size_t num_bits) const +FLATTEN ErrorOr SignedBigInteger::shift_left(size_t num_bits) const { - return SignedBigInteger { TRY(m_unsigned_data.try_shift_left(num_bits)), m_sign }; -} - -FLATTEN SignedBigInteger SignedBigInteger::shift_left(size_t num_bits) const -{ - return MUST(try_shift_left(num_bits)); + SignedBigInteger result; + MP_TRY(mp_mul_2d(&m_mp, num_bits, &result.m_mp)); + return result; } FLATTEN SignedBigInteger SignedBigInteger::shift_right(size_t num_bits) const { - return SignedBigInteger { m_unsigned_data.shift_right(num_bits), m_sign }; + SignedBigInteger result; + MP_MUST(mp_div_2d(&m_mp, num_bits, &result.m_mp, nullptr)); + return result; } FLATTEN ErrorOr SignedBigInteger::mod_power_of_two(size_t power_of_two) const { - auto const lower_bits = m_unsigned_data.as_n_bits(power_of_two); + if (power_of_two == 0) + return SignedBigInteger(0); - if (is_positive()) - return SignedBigInteger(lower_bits); + // If the number is positive and smaller than the modulus, we can just return it. + if (!is_negative() && static_cast(m_mp.used * MP_DIGIT_BIT) <= power_of_two) + return *this; - // twos encode lower bits - return SignedBigInteger(TRY(lower_bits.try_bitwise_not_fill_to_one_based_index(power_of_two)).plus(1).as_n_bits(power_of_two)); + // If the power of two overflows the int type, we don't have enough memory to compute it. + if (power_of_two > NumericLimits::max()) + return Error::from_errno(ENOMEM); + + SignedBigInteger result; + MP_MUST(mp_mod_2d(&m_mp, power_of_two, &result.m_mp)); + if (!result.is_negative()) + return result; + + // If the result is negative, we need to add the modulus to it. + UnsignedBigInteger modulus; + MP_TRY(mp_2expt(&modulus.m_mp, power_of_two)); + MP_MUST(mp_add(&result.m_mp, &modulus.m_mp, &result.m_mp)); + return result; } FLATTEN SignedBigInteger SignedBigInteger::multiplied_by(SignedBigInteger const& other) const { - bool result_sign = m_sign ^ other.m_sign; - return { m_unsigned_data.multiplied_by(other.m_unsigned_data), result_sign }; + SignedBigInteger result; + MP_MUST(mp_mul(&m_mp, &other.m_mp, &result.m_mp)); + return result; } FLATTEN SignedDivisionResult SignedBigInteger::divided_by(SignedBigInteger const& divisor) const { - // Aa / Bb -> (A^B)q, Ar - bool result_sign = m_sign ^ divisor.m_sign; - auto unsigned_division_result = m_unsigned_data.divided_by(divisor.m_unsigned_data); - return { - { move(unsigned_division_result.quotient), result_sign }, - { move(unsigned_division_result.remainder), m_sign } - }; + SignedBigInteger quotient; + SignedBigInteger remainder; + MP_MUST(mp_div(&m_mp, &divisor.m_mp, "ient.m_mp, &remainder.m_mp)); + return SignedDivisionResult { quotient, remainder }; } FLATTEN SignedBigInteger SignedBigInteger::pow(u32 exponent) const { - UnsignedBigInteger ep { exponent }; - SignedBigInteger base { *this }; - SignedBigInteger exp { 1 }; - - while (!(ep < 1)) { - if (ep.words()[0] % 2 == 1) - exp.set_to(exp.multiplied_by(base)); - - // ep = ep / 2; - ep.set_to(ep.shift_right(1)); - - // base = base * base - base.set_to(base.multiplied_by(base)); - } - - return exp; + SignedBigInteger result; + MP_MUST(mp_expt_n(&m_mp, exponent, &result.m_mp)); + return result; } FLATTEN SignedBigInteger SignedBigInteger::negated_value() const @@ -343,20 +333,18 @@ FLATTEN SignedBigInteger SignedBigInteger::negated_value() const u32 SignedBigInteger::hash() const { - return m_unsigned_data.hash() * (1 - (2 * m_sign)); -} + if (m_hash.has_value()) + return *m_hash; -void SignedBigInteger::set_bit_inplace(size_t bit_index) -{ - m_unsigned_data.set_bit_inplace(bit_index); + auto buffer = MUST(ByteBuffer::create_zeroed(byte_length())); + auto length = export_data(buffer); + m_hash = string_hash(reinterpret_cast(buffer.data()), length); + return *m_hash; } bool SignedBigInteger::operator==(SignedBigInteger const& other) const { - if (m_unsigned_data == 0 && other.m_unsigned_data == 0) - return true; - - return m_sign == other.m_sign && m_unsigned_data == other.m_unsigned_data; + return mp_cmp(&m_mp, &other.m_mp) == MP_EQ; } bool SignedBigInteger::operator!=(SignedBigInteger const& other) const @@ -366,13 +354,7 @@ bool SignedBigInteger::operator!=(SignedBigInteger const& other) const bool SignedBigInteger::operator<(SignedBigInteger const& other) const { - if (m_sign ^ other.m_sign) - return m_sign; - - if (m_sign) - return other.m_unsigned_data < m_unsigned_data; - - return m_unsigned_data < other.m_unsigned_data; + return mp_cmp(&m_mp, &other.m_mp) == MP_LT; } bool SignedBigInteger::operator<=(SignedBigInteger const& other) const @@ -392,7 +374,7 @@ bool SignedBigInteger::operator>=(SignedBigInteger const& other) const UnsignedBigInteger::CompareResult SignedBigInteger::compare_to_double(double value) const { - bool bigint_is_negative = m_sign; + bool bigint_is_negative = is_negative(); bool value_is_negative = value < 0; @@ -400,9 +382,9 @@ UnsignedBigInteger::CompareResult SignedBigInteger::compare_to_double(double val return bigint_is_negative ? UnsignedBigInteger::CompareResult::DoubleGreaterThanBigInt : UnsignedBigInteger::CompareResult::DoubleLessThanBigInt; // Now both bigint and value have the same sign, so let's compare our magnitudes. - auto magnitudes_compare_result = m_unsigned_data.compare_to_double(fabs(value)); + auto magnitudes_compare_result = unsigned_value().compare_to_double(fabs(value)); - // If our mangnitudes are euqal, then we're equal. + // If our magnitudes are equal, then we're equal. if (magnitudes_compare_result == UnsignedBigInteger::CompareResult::DoubleEqualsBigInt) return UnsignedBigInteger::CompareResult::DoubleEqualsBigInt; diff --git a/Libraries/LibCrypto/BigInt/SignedBigInteger.h b/Libraries/LibCrypto/BigInt/SignedBigInteger.h index bd036418d11..6db09b3aa73 100644 --- a/Libraries/LibCrypto/BigInt/SignedBigInteger.h +++ b/Libraries/LibCrypto/BigInt/SignedBigInteger.h @@ -1,14 +1,13 @@ /* * Copyright (c) 2020, the SerenityOS developers. * Copyright (c) 2022, David Tuin + * Copyright (c) 2025, Altomani Gianluca * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include -#include #include namespace Crypto { @@ -18,44 +17,27 @@ struct SignedDivisionResult; class SignedBigInteger { public: template - requires(sizeof(T) <= sizeof(i32)) SignedBigInteger(T value) - : m_sign(value < 0) - , m_unsigned_data(static_cast(abs(static_cast(value)))) - { - } - - SignedBigInteger(UnsignedBigInteger&& unsigned_data, bool sign) - : m_sign(sign) - , m_unsigned_data(move(unsigned_data)) - { - ensure_sign_is_valid(); - } - - explicit SignedBigInteger(UnsignedBigInteger unsigned_data) - : m_sign(false) - , m_unsigned_data(move(unsigned_data)) - { - } - - SignedBigInteger() - : m_sign(false) - , m_unsigned_data() + : SignedBigInteger(static_cast(value)) { } + SignedBigInteger(UnsignedBigInteger&& unsigned_data, bool sign); + SignedBigInteger(u8 const* ptr, size_t length); + explicit SignedBigInteger(UnsignedBigInteger const& unsigned_data); explicit SignedBigInteger(double value); + explicit SignedBigInteger(i64 value); - explicit SignedBigInteger(i64 value) - : m_sign(value < 0) - , m_unsigned_data(value < 0 ? static_cast(-(value + 1)) + 1 : static_cast(value)) - { - } + SignedBigInteger(SignedBigInteger const&); + SignedBigInteger& operator=(SignedBigInteger const&); - [[nodiscard]] static SignedBigInteger import_data(StringView data) { return import_data((u8 const*)data.characters_without_null_termination(), data.length()); } - [[nodiscard]] static SignedBigInteger import_data(u8 const* ptr, size_t length); + SignedBigInteger(); + ~SignedBigInteger(); - size_t export_data(Bytes, bool remove_leading_zeros = false) const; + [[nodiscard]] static SignedBigInteger import_data(StringView data) { return import_data(reinterpret_cast(data.characters_without_null_termination()), data.length()); } + [[nodiscard]] static SignedBigInteger import_data(u8 const* ptr, size_t length) { return SignedBigInteger(ptr, length); } + + size_t export_data(Bytes) const; [[nodiscard]] static ErrorOr from_base(u16 N, StringView str); [[nodiscard]] ErrorOr to_base(u16 N) const; @@ -63,38 +45,17 @@ public: [[nodiscard]] u64 to_u64() const; [[nodiscard]] double to_double(UnsignedBigInteger::RoundingMode rounding_mode = UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa) const; - [[nodiscard]] UnsignedBigInteger const& unsigned_value() const { return m_unsigned_data; } - [[nodiscard]] Vector const words() const { return m_unsigned_data.words(); } + [[nodiscard]] UnsignedBigInteger unsigned_value() const; [[nodiscard]] bool is_positive() const { return !is_negative() && !is_zero(); } - [[nodiscard]] bool is_negative() const { return m_sign; } - [[nodiscard]] bool is_zero() const { return m_unsigned_data.is_zero(); } + [[nodiscard]] bool is_negative() const; + [[nodiscard]] bool is_zero() const; - void negate() - { - if (!m_unsigned_data.is_zero()) - m_sign = !m_sign; - } + void negate(); + void set_to_0(); + void set_to(i64 other); + void set_to(SignedBigInteger const& other); - void set_to_0() - { - m_unsigned_data.set_to_0(); - m_sign = false; - } - - void set_to(i32 other) - { - m_unsigned_data.set_to((u32)other); - m_sign = other < 0; - } - void set_to(SignedBigInteger const& other) - { - m_unsigned_data.set_to(other.m_unsigned_data); - m_sign = other.m_sign; - } - - // These get + 1 byte for the sign. - [[nodiscard]] size_t length() const { return m_unsigned_data.length() + 1; } - [[nodiscard]] size_t trimmed_length() const { return m_unsigned_data.trimmed_length() + 1; } + [[nodiscard]] size_t byte_length() const; [[nodiscard]] SignedBigInteger plus(SignedBigInteger const& other) const; [[nodiscard]] SignedBigInteger minus(SignedBigInteger const& other) const; @@ -102,14 +63,12 @@ public: [[nodiscard]] SignedBigInteger bitwise_and(SignedBigInteger const& other) const; [[nodiscard]] SignedBigInteger bitwise_xor(SignedBigInteger const& other) const; [[nodiscard]] SignedBigInteger bitwise_not() const; - [[nodiscard]] SignedBigInteger shift_left(size_t num_bits) const; + [[nodiscard]] ErrorOr shift_left(size_t num_bits) const; [[nodiscard]] SignedBigInteger shift_right(size_t num_bits) const; [[nodiscard]] SignedBigInteger multiplied_by(SignedBigInteger const& other) const; [[nodiscard]] SignedDivisionResult divided_by(SignedBigInteger const& divisor) const; [[nodiscard]] SignedBigInteger pow(u32 exponent) const; - [[nodiscard]] ErrorOr mod_power_of_two(size_t power_of_two) const; - [[nodiscard]] ErrorOr try_shift_left(size_t num_bits) const; [[nodiscard]] SignedBigInteger plus(UnsignedBigInteger const& other) const; [[nodiscard]] SignedBigInteger minus(UnsignedBigInteger const& other) const; @@ -120,8 +79,6 @@ public: [[nodiscard]] u32 hash() const; - void set_bit_inplace(size_t bit_index); - [[nodiscard]] bool operator==(SignedBigInteger const& other) const; [[nodiscard]] bool operator!=(SignedBigInteger const& other) const; [[nodiscard]] bool operator<(SignedBigInteger const& other) const; @@ -137,14 +94,8 @@ public: [[nodiscard]] UnsignedBigInteger::CompareResult compare_to_double(double) const; private: - void ensure_sign_is_valid() - { - if (m_sign && m_unsigned_data.is_zero()) - m_sign = false; - } - - bool m_sign { false }; - UnsignedBigInteger m_unsigned_data; + mp_int m_mp {}; + mutable Optional m_hash {}; }; struct SignedDivisionResult { diff --git a/Libraries/LibCrypto/BigInt/Tommath.h b/Libraries/LibCrypto/BigInt/Tommath.h new file mode 100644 index 00000000000..eb1974144d7 --- /dev/null +++ b/Libraries/LibCrypto/BigInt/Tommath.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +#include + +inline ErrorOr mp_error(mp_err error) +{ + switch (error) { + case MP_OKAY: + return {}; + case MP_MEM: + return Error::from_errno(ENOMEM); + case MP_VAL: + return Error::from_errno(EINVAL); + case MP_ITER: + return Error::from_string_literal("Maximum iterations reached"); + case MP_BUF: + return Error::from_string_literal("Buffer overflow"); + default: + return Error::from_string_literal("Unknown error"); + } +} + +#define MP_TRY(...) TRY(mp_error((__VA_ARGS__))) + +#define MP_MUST(...) MUST(mp_error((__VA_ARGS__))) diff --git a/Libraries/LibCrypto/BigInt/TommathForward.h b/Libraries/LibCrypto/BigInt/TommathForward.h new file mode 100644 index 00000000000..e8f6fdfbcf8 --- /dev/null +++ b/Libraries/LibCrypto/BigInt/TommathForward.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +extern "C" { +typedef uint64_t mp_digit; +typedef int mp_sign; + +// This is a workaround for the fact that Tommath doesn't have a proper +// header file. It uses an anonymous struct to define the mp_int struct +// which makes it impossible to forward declare. +// Declare the mp_int struct with the same layout as the one in tommath.h +// and check that the layout is the same while avoiding a conflicting +// definition error (BN_H_ is defined in tommath.h). +// When importing this file it is important to always do it AFTER tommath.h. + +typedef struct { + int used, alloc; + mp_sign sign; + mp_digit* dp; +} mp_int_; + +#ifndef BN_H_ +typedef mp_int_ mp_int; +#else +static_assert(sizeof(mp_int_) == sizeof(mp_int)); +static_assert(offsetof(mp_int_, used) == offsetof(mp_int, used)); +static_assert(offsetof(mp_int_, alloc) == offsetof(mp_int, alloc)); +static_assert(offsetof(mp_int_, sign) == offsetof(mp_int, sign)); +static_assert(offsetof(mp_int_, dp) == offsetof(mp_int, dp)); +#endif +} diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index 7a8f6818dcf..4efa582a9fb 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -1,38 +1,33 @@ /* * Copyright (c) 2020, Itamar S. * Copyright (c) 2022, David Tuin + * Copyright (c) 2025, Altomani Gianluca * * SPDX-License-Identifier: BSD-2-Clause */ -#include "UnsignedBigInteger.h" -#include -#include -#include -#include -#include -#include #include +#include + +#include +#include +#include +#include +#include +#include namespace Crypto { UnsignedBigInteger::UnsignedBigInteger(u8 const* ptr, size_t length) { - m_words.resize_and_keep_capacity((length + sizeof(u32) - 1) / sizeof(u32)); - size_t in = length, out = 0; - while (in >= sizeof(u32)) { - in -= sizeof(u32); - u32 word = ((u32)ptr[in] << 24) | ((u32)ptr[in + 1] << 16) | ((u32)ptr[in + 2] << 8) | (u32)ptr[in + 3]; - m_words[out++] = word; - } - if (in > 0) { - u32 word = 0; - for (size_t i = 0; i < in; i++) { - word <<= 8; - word |= (u32)ptr[i]; - } - m_words[out++] = word; - } + MP_MUST(mp_init(&m_mp)); + MP_MUST(mp_from_ubin(&m_mp, ptr, length)); +} + +UnsignedBigInteger::UnsignedBigInteger(Vector const& words) +{ + MP_MUST(mp_init(&m_mp)); + MP_MUST(mp_unpack(&m_mp, words.size(), MP_LSB_FIRST, sizeof(u32), MP_NATIVE_ENDIAN, 0, words.data())); } UnsignedBigInteger::UnsignedBigInteger(double value) @@ -44,489 +39,265 @@ UnsignedBigInteger::UnsignedBigInteger(double value) VERIFY(trunc(value) == value); VERIFY(value >= 0.0); - if (value <= NumericLimits::max()) { - m_words.append(static_cast(value)); - return; - } - - FloatExtractor extractor; - extractor.d = value; - VERIFY(!extractor.sign); - - i32 real_exponent = extractor.exponent - extractor.exponent_bias; - VERIFY(real_exponent > 0); - - // Ensure we have enough space, we will need 2^exponent bits, so round up in words - auto word_index = (real_exponent + BITS_IN_WORD) / BITS_IN_WORD; - m_words.resize_and_keep_capacity(word_index); - - // Now we just need to put the mantissa with explicit 1 bit at the top at the proper location - u64 raw_mantissa = extractor.mantissa | (1ull << extractor.mantissa_bits); - VERIFY((raw_mantissa & 0xfff0000000000000) == 0x0010000000000000); - // Shift it so the bits we need are at the top - raw_mantissa <<= 64 - extractor.mantissa_bits - 1; - - // The initial bit needs to be exactly aligned with exponent, this is 1-indexed - auto top_word_bit_offset = real_exponent % BITS_IN_WORD + 1; - - auto top_word_bits_from_mantissa = raw_mantissa >> (64 - top_word_bit_offset); - VERIFY(top_word_bits_from_mantissa <= NumericLimits::max()); - m_words[word_index - 1] = top_word_bits_from_mantissa; - - --word_index; - // Shift used bits away - raw_mantissa <<= top_word_bit_offset; - i32 bits_in_mantissa = extractor.mantissa_bits + 1 - top_word_bit_offset; - // Now just put everything at the top of the next words - - constexpr auto to_word_shift = 64 - BITS_IN_WORD; - - while (word_index > 0 && bits_in_mantissa > 0) { - VERIFY((raw_mantissa >> to_word_shift) <= NumericLimits::max()); - m_words[word_index - 1] = raw_mantissa >> to_word_shift; - raw_mantissa <<= to_word_shift; - - bits_in_mantissa -= BITS_IN_WORD; - --word_index; - } - - VERIFY(m_words.size() > word_index); - VERIFY((m_words.size() - word_index) <= 3); - - // No bits left, otherwise we would have to round - VERIFY(raw_mantissa == 0); + MP_MUST(mp_init(&m_mp)); + MP_MUST(mp_set_double(&m_mp, value)); } -size_t UnsignedBigInteger::export_data(Bytes data, bool remove_leading_zeros) const +UnsignedBigInteger::UnsignedBigInteger(u64 value) { - size_t word_count = trimmed_length(); - size_t out = 0; - if (word_count > 0) { - ssize_t leading_zeros = -1; - if (remove_leading_zeros) { - UnsignedBigInteger::Word word = m_words[word_count - 1]; - for (size_t i = 0; i < sizeof(u32); i++) { - u8 byte = (u8)(word >> ((sizeof(u32) - i - 1) * 8)); - data[out++] = byte; - if (leading_zeros < 0 && byte != 0) - leading_zeros = (int)i; - } - } - for (size_t i = word_count - (remove_leading_zeros ? 1 : 0); i > 0; i--) { - auto word = m_words[i - 1]; - data[out++] = (u8)(word >> 24); - data[out++] = (u8)(word >> 16); - data[out++] = (u8)(word >> 8); - data[out++] = (u8)word; - } - if (leading_zeros > 0) - out -= leading_zeros; - } - return out; + MP_MUST(mp_init(&m_mp)); + mp_set_u64(&m_mp, value); +} + +UnsignedBigInteger::UnsignedBigInteger(UnsignedBigInteger const& other) +{ + MP_MUST(mp_init_copy(&m_mp, &other.m_mp)); +} + +UnsignedBigInteger& UnsignedBigInteger::operator=(UnsignedBigInteger const& other) +{ + if (this == &other) + return *this; + mp_clear(&m_mp); + MP_MUST(mp_init_copy(&m_mp, &other.m_mp)); + return *this; +} + +UnsignedBigInteger::UnsignedBigInteger() +{ + MP_MUST(mp_init(&m_mp)); +} + +UnsignedBigInteger::~UnsignedBigInteger() +{ + mp_clear(&m_mp); +} + +size_t UnsignedBigInteger::export_data(Bytes data) const +{ + size_t written = 0; + MP_MUST(mp_to_ubin(&m_mp, data.data(), data.size(), &written)); + return written; } ErrorOr UnsignedBigInteger::from_base(u16 N, StringView str) { VERIFY(N <= 36); - UnsignedBigInteger result; - UnsignedBigInteger base { N }; + if (str.is_empty()) + return UnsignedBigInteger(0); - for (auto const& c : str) { - if (c == '_') + auto buffer = TRY(ByteBuffer::create_zeroed(str.length() + 1)); + + size_t idx = 0; + for (auto& c : str) { + if (c == '_') { + // Skip underscores continue; - if (!is_ascii_base36_digit(c)) - return Error::from_string_literal("Invalid Base36 digit"); - auto digit = parse_ascii_base36_digit(c); - if (digit >= N) - return Error::from_string_literal("Base36 digit out of range"); + } - result = result.multiplied_by(base).plus(digit); + buffer[idx++] = c; } + + UnsignedBigInteger result; + if (mp_read_radix(&result.m_mp, reinterpret_cast(buffer.data()), N) != MP_OKAY) + return Error::from_string_literal("Invalid number"); return result; } ErrorOr UnsignedBigInteger::to_base(u16 N) const { VERIFY(N <= 36); - if (*this == UnsignedBigInteger { 0 }) + if (is_zero()) return "0"_string; - StringBuilder builder; - UnsignedBigInteger temp(*this); - UnsignedBigInteger quotient; - UnsignedBigInteger remainder; + int size = 0; + MP_MUST(mp_radix_size(&m_mp, N, &size)); + auto buffer = TRY(ByteBuffer::create_zeroed(size)); - while (temp != UnsignedBigInteger { 0 }) { - UnsignedBigIntegerAlgorithms::divide_u16_without_allocation(temp, N, quotient, remainder); - VERIFY(remainder.words()[0] < N); - TRY(builder.try_append(to_ascii_base36_digit(remainder.words()[0]))); - temp.set_to(quotient); - } + size_t written = 0; + MP_MUST(mp_to_radix(&m_mp, reinterpret_cast(buffer.data()), size, &written, N)); - return TRY(builder.to_string()).reverse(); + return StringView(buffer.bytes().slice(0, written - 1)).to_ascii_lowercase_string(); } u64 UnsignedBigInteger::to_u64() const { - static_assert(sizeof(Word) == 4); - if (!length()) - return 0; - u64 value = m_words[0]; - if (length() > 1) - value |= static_cast(m_words[1]) << 32; - return value; + return mp_get_u64(&m_mp); } -double UnsignedBigInteger::to_double(UnsignedBigInteger::RoundingMode rounding_mode) const +double UnsignedBigInteger::to_double(RoundingMode rounding_mode) const { - auto highest_bit = one_based_index_of_highest_set_bit(); - if (highest_bit == 0) - return 0; - --highest_bit; + // Check if we need to truncate + auto bitlen = mp_count_bits(&m_mp); + if (bitlen <= 53) + return mp_get_double(&m_mp); - using Extractor = FloatExtractor; + if (rounding_mode == RoundingMode::RoundTowardZero) { + UnsignedBigInteger shifted; - // Simple case if less than 2^53 since those number are all exactly representable in doubles - if (highest_bit < Extractor::mantissa_bits + 1) - return static_cast(to_u64()); + // Truncate the lower bits + auto shift = bitlen - 53; + MP_MUST(mp_div_2d(&m_mp, shift, &shifted.m_mp, nullptr)); - // If it uses too many bit to represent in a double return infinity - if (highest_bit > Extractor::exponent_bias) - return __builtin_huge_val(); - - // Otherwise we have to take the top 53 bits, use those as the mantissa, - // and the amount of bits as the exponent. Note that the mantissa has an implicit top bit of 1 - // so we have to ignore the very top bit. - - // Since we extract at most 53 bits it will take at most 3 words - static_assert(BITS_IN_WORD * 3 >= (Extractor::mantissa_bits + 1)); - constexpr auto bits_in_u64 = 64; - static_assert(bits_in_u64 > Extractor::mantissa_bits + 1); - - auto bits_to_read = min(static_cast(Extractor::mantissa_bits), highest_bit); - - auto last_word_index = trimmed_length(); - VERIFY(last_word_index > 0); - - // Note that highest bit is 0-indexed at this point. - auto highest_bit_index_in_top_word = highest_bit % BITS_IN_WORD; - - // Shift initial word until highest bit is just beyond top of u64. - u64 mantissa = m_words[last_word_index - 1]; - if (highest_bit_index_in_top_word != 0) - mantissa <<= (bits_in_u64 - highest_bit_index_in_top_word); - else - mantissa = 0; - - auto bits_written = highest_bit_index_in_top_word; - - --last_word_index; - - Optional dropped_bits_for_rounding; - u8 bits_dropped_from_final_word = 0; - - if (bits_written < bits_to_read && last_word_index > 0) { - // Second word can always just cleanly be shifted up to the final bit of the first word - // since the first has at most BIT_IN_WORD - 1, 31 - u64 next_word = m_words[last_word_index - 1]; - VERIFY((mantissa & (next_word << (bits_in_u64 - bits_written - BITS_IN_WORD))) == 0); - mantissa |= next_word << (bits_in_u64 - bits_written - BITS_IN_WORD); - bits_written += BITS_IN_WORD; - --last_word_index; - - if (bits_written > bits_to_read) { - bits_dropped_from_final_word = bits_written - bits_to_read; - dropped_bits_for_rounding = m_words[last_word_index] & ((1 << bits_dropped_from_final_word) - 1); - } else if (bits_written < bits_to_read && last_word_index > 0) { - // The final word has to be shifted down first to discard any excess bits. - u64 final_word = m_words[last_word_index - 1]; - --last_word_index; - - auto bits_to_write = bits_to_read - bits_written; - - bits_dropped_from_final_word = BITS_IN_WORD - bits_to_write; - dropped_bits_for_rounding = final_word & ((1 << bits_dropped_from_final_word) - 1u); - final_word >>= bits_dropped_from_final_word; - - // Then move the bits right up to the lowest bits of the second word - VERIFY((mantissa & (final_word << (bits_in_u64 - bits_written - bits_to_write))) == 0); - mantissa |= final_word << (bits_in_u64 - bits_written - bits_to_write); - } + // Convert to double + return ldexp(mp_get_double(&shifted.m_mp), shift); } - // Now the mantissa should be complete so shift it down - mantissa >>= bits_in_u64 - Extractor::mantissa_bits; - if (rounding_mode == RoundingMode::IEEERoundAndTiesToEvenMantissa) { - bool round_up = false; + UnsignedBigInteger shifted, remainder, half, lsb; - if (bits_dropped_from_final_word == 0) { - if (last_word_index > 0) { - Word next_word = m_words[last_word_index - 1]; - last_word_index--; - if ((next_word & 0x80000000) != 0) { - // next top bit set check for any other bits - if ((next_word ^ 0x80000000) != 0) { - round_up = true; - } else { - while (last_word_index > 0) { - if (m_words[last_word_index - 1] != 0) { - round_up = true; - break; - } - } - - // All other bits are 0 which is a tie thus round to even exponent - // Since we are halfway, if exponent ends with 1 we round up, if 0 we round down - round_up = (mantissa & 1) != 0; - } - } else { - round_up = false; - } - } else { - // If there are no words left the rest is implicitly 0 so just round down - round_up = false; - } + // Get top 53 bits (truncated) + auto shift = bitlen - 53; + MP_MUST(mp_div_2d(&m_mp, shift, &shifted.m_mp, &remainder.m_mp)); + // Check if remainder == 2^(shift - 1) + MP_MUST(mp_2expt(&half.m_mp, shift - 1)); + int cmp = mp_cmp(&remainder.m_mp, &half.m_mp); + if (cmp < 0) { + // Round down (truncate) + } else if (cmp > 0) { + // Round up + MP_MUST(mp_add_d(&shifted.m_mp, 1, &shifted.m_mp)); } else { - VERIFY(dropped_bits_for_rounding.has_value()); - VERIFY(bits_dropped_from_final_word >= 1); - - // In this case the top bit comes form the dropped bits - auto top_bit_extractor = 1u << (bits_dropped_from_final_word - 1u); - if ((*dropped_bits_for_rounding & top_bit_extractor) != 0) { - // Possible tie again, if any other bit is set we round up - if ((*dropped_bits_for_rounding ^ top_bit_extractor) != 0) { - round_up = true; - } else { - while (last_word_index > 0) { - if (m_words[last_word_index - 1] != 0) { - round_up = true; - break; - } - } - - round_up = (mantissa & 1) != 0; - } - } else { - round_up = false; + // Exactly halfway, check even + MP_MUST(mp_mod_2d(&shifted.m_mp, 1, &lsb.m_mp)); + if (!mp_iszero(&lsb.m_mp)) { + // It's odd, round to even + MP_MUST(mp_add_d(&shifted.m_mp, 1, &shifted.m_mp)); } } - if (round_up) { - ++mantissa; - if ((mantissa & (1ull << Extractor::mantissa_bits)) != 0) { - // we overflowed the mantissa - mantissa = 0; - highest_bit++; - - // In which case it is possible we have to round to infinity - if (highest_bit > Extractor::exponent_bias) - return __builtin_huge_val(); - } - } - } else { - VERIFY(rounding_mode == RoundingMode::RoundTowardZero); + // Convert to double + return ldexp(mp_get_double(&shifted.m_mp), shift); } - Extractor extractor; - extractor.exponent = highest_bit + extractor.exponent_bias; + VERIFY_NOT_REACHED(); +} - VERIFY((mantissa & 0xfff0000000000000) == 0); - extractor.mantissa = mantissa; +Vector UnsignedBigInteger::words() const +{ + auto count = mp_pack_count(&m_mp, 0, sizeof(u32)); + Vector result; + result.resize(count); - return extractor.d; + size_t written = 0; + MP_MUST(mp_pack(result.data(), count, &written, MP_LSB_FIRST, sizeof(u32), MP_NATIVE_ENDIAN, 0, &m_mp)); + + result.resize(written); + return result; } void UnsignedBigInteger::set_to_0() { - m_words.clear_with_capacity(); - m_cached_trimmed_length = {}; - m_cached_hash = 0; + mp_zero(&m_mp); + m_hash = {}; } -void UnsignedBigInteger::set_to(UnsignedBigInteger::Word other) +void UnsignedBigInteger::set_to(u64 other) { - m_words.resize_and_keep_capacity(1); - m_words[0] = other; - m_cached_trimmed_length = {}; - m_cached_hash = 0; + mp_set_u64(&m_mp, other); + m_hash = {}; } void UnsignedBigInteger::set_to(UnsignedBigInteger const& other) { - m_words.resize_and_keep_capacity(other.m_words.size()); - __builtin_memcpy(m_words.data(), other.m_words.data(), other.m_words.size() * sizeof(u32)); - m_cached_trimmed_length = {}; - m_cached_hash = 0; + MP_MUST(mp_copy(&other.m_mp, &m_mp)); + m_hash = {}; } bool UnsignedBigInteger::is_zero() const { - for (size_t i = 0; i < length(); ++i) { - if (m_words[i] != 0) - return false; - } - - return true; + return mp_iszero(&m_mp); } -size_t UnsignedBigInteger::trimmed_length() const +bool UnsignedBigInteger::is_odd() const { - if (!m_cached_trimmed_length.has_value()) { - size_t num_leading_zeroes = 0; - for (int i = length() - 1; i >= 0; --i, ++num_leading_zeroes) { - if (m_words[i] != 0) - break; - } - m_cached_trimmed_length = length() - num_leading_zeroes; - } - return m_cached_trimmed_length.value(); + return mp_isodd(&m_mp); } -void UnsignedBigInteger::clamp_to_trimmed_length() +size_t UnsignedBigInteger::byte_length() const { - auto length = trimmed_length(); - if (m_words.size() > 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)); - } + return mp_ubin_size(&m_mp); } size_t UnsignedBigInteger::one_based_index_of_highest_set_bit() const { - size_t number_of_words = trimmed_length(); - size_t index = 0; - if (number_of_words > 0) { - index += (number_of_words - 1) * BITS_IN_WORD; - index += BITS_IN_WORD - count_leading_zeroes(m_words[number_of_words - 1]); - } - return index; + return mp_count_bits(&m_mp); } FLATTEN UnsignedBigInteger UnsignedBigInteger::plus(UnsignedBigInteger const& other) const { UnsignedBigInteger result; - - UnsignedBigIntegerAlgorithms::add_without_allocation(*this, other, result); - + MP_MUST(mp_add(&m_mp, &other.m_mp, &result.m_mp)); return result; } FLATTEN ErrorOr UnsignedBigInteger::minus(UnsignedBigInteger const& other) const { UnsignedBigInteger result; - - TRY(UnsignedBigIntegerAlgorithms::subtract_without_allocation(*this, other, result)); - + MP_MUST(mp_sub(&m_mp, &other.m_mp, &result.m_mp)); + if (mp_isneg(&result.m_mp)) + return Error::from_string_literal("Substraction produced a negative result"); return result; } FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_or(UnsignedBigInteger const& other) const { UnsignedBigInteger result; - - UnsignedBigIntegerAlgorithms::bitwise_or_without_allocation(*this, other, result); - + MP_MUST(mp_or(&m_mp, &other.m_mp, &result.m_mp)); return result; } FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_and(UnsignedBigInteger const& other) const { UnsignedBigInteger result; - - UnsignedBigIntegerAlgorithms::bitwise_and_without_allocation(*this, other, result); - + MP_MUST(mp_and(&m_mp, &other.m_mp, &result.m_mp)); return result; } FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_xor(UnsignedBigInteger const& other) const { UnsignedBigInteger result; + MP_MUST(mp_xor(&m_mp, &other.m_mp, &result.m_mp)); + return result; +} - UnsignedBigIntegerAlgorithms::bitwise_xor_without_allocation(*this, other, result); +FLATTEN ErrorOr UnsignedBigInteger::bitwise_not_fill_to_one_based_index(size_t index) const +{ + if (index == 0) + return UnsignedBigInteger(0); + if (index > NumericLimits::max()) + return Error::from_errno(ENOMEM); + + UnsignedBigInteger result, mask, temp; + + MP_TRY(mp_2expt(&mask.m_mp, index)); + MP_TRY(mp_sub_d(&mask.m_mp, 1, &mask.m_mp)); + + MP_TRY(mp_and(&mask.m_mp, &m_mp, &temp.m_mp)); + MP_TRY(mp_xor(&temp.m_mp, &mask.m_mp, &result.m_mp)); return result; } -FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_not_fill_to_one_based_index(size_t size) const -{ - return MUST(try_bitwise_not_fill_to_one_based_index(size)); -} - -FLATTEN ErrorOr UnsignedBigInteger::try_bitwise_not_fill_to_one_based_index(size_t size) const +FLATTEN ErrorOr UnsignedBigInteger::shift_left(size_t num_bits) const { UnsignedBigInteger result; - - TRY(UnsignedBigIntegerAlgorithms::bitwise_not_fill_to_one_based_index_without_allocation(*this, size, result)); - + MP_TRY(mp_mul_2d(&m_mp, num_bits, &result.m_mp)); return result; } -FLATTEN ErrorOr UnsignedBigInteger::try_shift_left(size_t num_bits) const -{ - UnsignedBigInteger output; - - TRY(UnsignedBigIntegerAlgorithms::try_shift_left_without_allocation(*this, num_bits, output)); - - return output; -} - -FLATTEN UnsignedBigInteger UnsignedBigInteger::shift_left(size_t num_bits) const -{ - return MUST(try_shift_left(num_bits)); -} - FLATTEN UnsignedBigInteger UnsignedBigInteger::shift_right(size_t num_bits) const { - UnsignedBigInteger output; - - UnsignedBigIntegerAlgorithms::shift_right_without_allocation(*this, num_bits, output); - - return output; -} - -FLATTEN UnsignedBigInteger UnsignedBigInteger::as_n_bits(size_t n) const -{ - if (auto const num_bits = one_based_index_of_highest_set_bit(); n >= num_bits) - return *this; - - UnsignedBigInteger output; - output.set_to(*this); - - auto const word_index = n / BITS_IN_WORD; - - auto const bits_to_keep = n % BITS_IN_WORD; - auto const bits_to_discard = BITS_IN_WORD - bits_to_keep; - - output.m_words.resize(word_index + 1); - - auto const last_word = output.m_words[word_index]; - Word new_last_word = 0; - - // avoid UB from a 32 bit shift on a u32 - if (bits_to_keep != 0) - new_last_word = last_word << bits_to_discard >> bits_to_discard; - - output.m_words[word_index] = new_last_word; - - return output; + UnsignedBigInteger result; + MP_MUST(mp_div_2d(&m_mp, num_bits, &result.m_mp, nullptr)); + return result; } FLATTEN UnsignedBigInteger UnsignedBigInteger::multiplied_by(UnsignedBigInteger const& other) const { UnsignedBigInteger result; - UnsignedBigInteger temp_shift; - - UnsignedBigIntegerAlgorithms::multiply_without_allocation(*this, other, temp_shift, result); - + MP_MUST(mp_mul(&m_mp, &other.m_mp, &result.m_mp)); return result; } @@ -534,84 +305,38 @@ FLATTEN UnsignedDivisionResult UnsignedBigInteger::divided_by(UnsignedBigInteger { UnsignedBigInteger quotient; UnsignedBigInteger remainder; - - // 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)) { - UnsignedBigIntegerAlgorithms::divide_u16_without_allocation(*this, divisor.m_words[0], quotient, remainder); - return UnsignedDivisionResult { quotient, remainder }; - } - - UnsignedBigIntegerAlgorithms::divide_without_allocation(*this, divisor, quotient, remainder); - + MP_MUST(mp_div(&m_mp, &divisor.m_mp, "ient.m_mp, &remainder.m_mp)); return UnsignedDivisionResult { quotient, remainder }; } FLATTEN UnsignedBigInteger UnsignedBigInteger::pow(u32 exponent) const { - UnsignedBigInteger ep { exponent }; - UnsignedBigInteger base { *this }; - UnsignedBigInteger exp { 1 }; - - while (!(ep < 1 )) { - if (ep.words()[0] % 2 == 1) - exp.set_to(exp.multiplied_by(base)); - - // ep = ep / 2; - ep.set_to(ep.shift_right(1)); - - // base = base * base - base.set_to(base.multiplied_by(base)); - } - - return exp; + UnsignedBigInteger result; + MP_MUST(mp_expt_n(&m_mp, exponent, &result.m_mp)); + return result; } FLATTEN UnsignedBigInteger UnsignedBigInteger::gcd(UnsignedBigInteger const& other) const { - UnsignedBigInteger temp_a { *this }; - UnsignedBigInteger temp_b { other }; - UnsignedBigInteger temp_quotient; - UnsignedBigInteger temp_remainder; - UnsignedBigInteger output; - - UnsignedBigIntegerAlgorithms::destructive_GCD_without_allocation(temp_a, temp_b, temp_quotient, temp_remainder, output); - - return output; + UnsignedBigInteger result; + MP_MUST(mp_gcd(&m_mp, &other.m_mp, &result.m_mp)); + return result; } u32 UnsignedBigInteger::hash() const { - if (m_cached_hash != 0) - return m_cached_hash; + if (m_hash.has_value()) + return *m_hash; - return m_cached_hash = string_hash((char const*)m_words.data(), sizeof(Word) * m_words.size()); -} - -void UnsignedBigInteger::set_bit_inplace(size_t bit_index) -{ - size_t const word_index = bit_index / UnsignedBigInteger::BITS_IN_WORD; - size_t const inner_word_index = bit_index % UnsignedBigInteger::BITS_IN_WORD; - - m_words.ensure_capacity(word_index + 1); - - for (size_t i = length(); i <= word_index; ++i) { - m_words.unchecked_append(0); - } - m_words[word_index] |= (1 << inner_word_index); - - m_cached_trimmed_length = {}; - m_cached_hash = 0; + auto buffer = MUST(ByteBuffer::create_zeroed(byte_length())); + auto length = export_data(buffer); + m_hash = string_hash(reinterpret_cast(buffer.data()), length); + return *m_hash; } bool UnsignedBigInteger::operator==(UnsignedBigInteger const& other) const { - auto length = trimmed_length(); - - if (length != other.trimmed_length()) - return false; - - return !__builtin_memcmp(m_words.data(), other.words().data(), length * (BITS_IN_WORD / 8)); + return mp_cmp(&m_mp, &other.m_mp) == MP_EQ; } bool UnsignedBigInteger::operator!=(UnsignedBigInteger const& other) const @@ -621,26 +346,7 @@ bool UnsignedBigInteger::operator!=(UnsignedBigInteger const& other) const bool UnsignedBigInteger::operator<(UnsignedBigInteger const& other) const { - auto length = trimmed_length(); - auto other_length = other.trimmed_length(); - - if (length < other_length) { - return true; - } - - if (length > other_length) { - return false; - } - - if (length == 0) { - return false; - } - 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; + return mp_cmp(&m_mp, &other.m_mp) == MP_LT; } bool UnsignedBigInteger::operator<=(UnsignedBigInteger const& other) const @@ -720,21 +426,24 @@ UnsignedBigInteger::CompareResult UnsignedBigInteger::compare_to_double(double v mantissa_bits |= mantissa_extended_bit; + constexpr u32 bits_in_word = sizeof(u32) * 8; + // Now we shift value to the left virtually, with `exponent - bias` steps // we then pretend both it and the big int are extended with virtual zeros. - auto next_bigint_word = (BITS_IN_WORD - 1 + bigint_bits_needed) / BITS_IN_WORD; + auto next_bigint_word = (bits_in_word - 1 + bigint_bits_needed) / bits_in_word; - VERIFY(next_bigint_word == trimmed_length()); + auto words = this->words(); + VERIFY(next_bigint_word == words.size()); - auto msb_in_top_word_index = (bigint_bits_needed - 1) % BITS_IN_WORD; - VERIFY(msb_in_top_word_index == (BITS_IN_WORD - count_leading_zeroes(words()[next_bigint_word - 1]) - 1)); + auto msb_in_top_word_index = (bigint_bits_needed - 1) % bits_in_word; + VERIFY(msb_in_top_word_index == (bits_in_word - count_leading_zeroes(words[next_bigint_word - 1]) - 1)); // We will keep the bits which are still valid in the mantissa at the top of mantissa bits. mantissa_bits <<= 64 - (extractor.mantissa_bits + 1); auto bits_left_in_mantissa = static_cast(extractor.mantissa_bits) + 1; - auto get_next_value_bits = [&](size_t num_bits) -> Word { + auto get_next_value_bits = [&](size_t num_bits) -> u32 { VERIFY(num_bits < 63); VERIFY(bits_left_in_mantissa > 0); if (num_bits > bits_left_in_mantissa) @@ -745,24 +454,24 @@ UnsignedBigInteger::CompareResult UnsignedBigInteger::compare_to_double(double v u64 extracted_bits = mantissa_bits & (((1ull << num_bits) - 1) << (64 - num_bits)); // Now shift the bits down to put the most significant bit on the num_bits position // this means the rest will be "virtual" zeros. - extracted_bits >>= 32; + extracted_bits >>= bits_in_word; // Now shift away the used bits and fit the result into a Word. mantissa_bits <<= num_bits; - VERIFY(extracted_bits <= NumericLimits::max()); - return static_cast(extracted_bits); + VERIFY(extracted_bits <= NumericLimits::max()); + return static_cast(extracted_bits); }; auto bits_in_next_bigint_word = msb_in_top_word_index + 1; while (next_bigint_word > 0 && bits_left_in_mantissa > 0) { - Word bigint_word = words()[next_bigint_word - 1]; - Word double_word = get_next_value_bits(bits_in_next_bigint_word); + u32 bigint_word = words[next_bigint_word - 1]; + u32 double_word = get_next_value_bits(bits_in_next_bigint_word); // For the first bit we have to align it with the top bit of bigint // and for all the other cases bits_in_next_bigint_word is 32 so this does nothing. - double_word >>= 32 - bits_in_next_bigint_word; + double_word >>= bits_in_word - bits_in_next_bigint_word; if (bigint_word < double_word) return CompareResult::DoubleGreaterThanBigInt; @@ -771,14 +480,14 @@ UnsignedBigInteger::CompareResult UnsignedBigInteger::compare_to_double(double v return CompareResult::DoubleLessThanBigInt; --next_bigint_word; - bits_in_next_bigint_word = BITS_IN_WORD; + bits_in_next_bigint_word = bits_in_word; } // If there are still bits left in bigint than any non zero bit means it has greater value. if (next_bigint_word > 0) { VERIFY(bits_left_in_mantissa == 0); while (next_bigint_word > 0) { - if (words()[next_bigint_word - 1] != 0) + if (words[next_bigint_word - 1] != 0) return CompareResult::DoubleLessThanBigInt; --next_bigint_word; } @@ -797,9 +506,5 @@ UnsignedBigInteger::CompareResult UnsignedBigInteger::compare_to_double(double v ErrorOr AK::Formatter::format(FormatBuilder& fmtbuilder, Crypto::UnsignedBigInteger const& value) { - StringBuilder builder; - for (int i = value.length() - 1; i >= 0; --i) - TRY(builder.try_appendff("{}|", value.words()[i])); - - return Formatter::format(fmtbuilder, builder.string_view()); + return Formatter::format(fmtbuilder, TRY(value.to_base(10))); } diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index e4d63dedbfc..a2ba5d66dbf 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -2,66 +2,44 @@ * Copyright (c) 2020, Itamar S. * Copyright (c) 2022, the SerenityOS developers. * Copyright (c) 2022, David Tuin + * Copyright (c) 2025, Altomani Gianluca * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include #include -#include -#include #include -#include -#include +#include namespace Crypto { struct UnsignedDivisionResult; -constexpr size_t STARTING_WORD_SIZE = 32; class UnsignedBigInteger { public: - using Word = u32; - using StorageSpan = AK::Detail::StorageSpan; - using ConstStorageSpan = AK::Detail::StorageSpan; - static constexpr size_t BITS_IN_WORD = 32; - - // This constructor accepts any unsigned with size up to Word. template - requires(sizeof(T) <= sizeof(Word)) UnsignedBigInteger(T value) - { - m_words.append(static_cast(value)); - } - - explicit UnsignedBigInteger(Vector&& words) - : m_words(move(words)) + : UnsignedBigInteger(static_cast(value)) { } + UnsignedBigInteger(u8 const* ptr, size_t length); - explicit UnsignedBigInteger(u8 const* ptr, size_t length); - + explicit UnsignedBigInteger(Vector const& words); explicit UnsignedBigInteger(double value); + explicit UnsignedBigInteger(u64 value); - explicit UnsignedBigInteger(u64 value) - { - static_assert(sizeof(u64) == sizeof(Word) * 2); - m_words.resize_and_keep_capacity(2); - m_words[0] = static_cast(value & 0xFFFFFFFF); - m_words[1] = static_cast((value >> 32) & 0xFFFFFFFF); - } + UnsignedBigInteger(UnsignedBigInteger const&); + UnsignedBigInteger& operator=(UnsignedBigInteger const&); - UnsignedBigInteger() = default; + UnsignedBigInteger(); + ~UnsignedBigInteger(); - [[nodiscard]] static UnsignedBigInteger import_data(StringView data) { return import_data((u8 const*)data.characters_without_null_termination(), data.length()); } - [[nodiscard]] static UnsignedBigInteger import_data(u8 const* ptr, size_t length) - { - return UnsignedBigInteger(ptr, length); - } + [[nodiscard]] static UnsignedBigInteger import_data(StringView data) { return import_data(reinterpret_cast(data.characters_without_null_termination()), data.length()); } + [[nodiscard]] static UnsignedBigInteger import_data(u8 const* ptr, size_t length) { return UnsignedBigInteger(ptr, length); } - size_t export_data(Bytes, bool remove_leading_zeros = false) const; + size_t export_data(Bytes) const; [[nodiscard]] static ErrorOr from_base(u16 N, StringView str); [[nodiscard]] ErrorOr to_base(u16 N) const; @@ -77,24 +55,16 @@ public: [[nodiscard]] double to_double(RoundingMode rounding_mode = RoundingMode::IEEERoundAndTiesToEvenMantissa) const; - [[nodiscard]] Vector const& words() const { return m_words; } + [[nodiscard]] Vector words() const; void set_to_0(); - void set_to(Word other); + void set_to(u64 other); void set_to(UnsignedBigInteger const& other); [[nodiscard]] bool is_zero() const; - [[nodiscard]] bool is_odd() const { return m_words.size() && (m_words[0] & 1); } + [[nodiscard]] bool is_odd() const; - [[nodiscard]] size_t length() const { return m_words.size(); } - // The "trimmed length" is the number of words after trimming leading zeroed words - [[nodiscard]] size_t trimmed_length() const; - - [[nodiscard]] size_t byte_length() const { return length() * sizeof(Word); } - [[nodiscard]] size_t trimmed_byte_length() const { return trimmed_length() * sizeof(Word); } - - void clamp_to_trimmed_length(); - void resize_with_leading_zeros(size_t num_words); + [[nodiscard]] size_t byte_length() const; size_t one_based_index_of_highest_set_bit() const; @@ -103,22 +73,16 @@ public: [[nodiscard]] UnsignedBigInteger bitwise_or(UnsignedBigInteger const& other) const; [[nodiscard]] UnsignedBigInteger bitwise_and(UnsignedBigInteger const& other) const; [[nodiscard]] UnsignedBigInteger bitwise_xor(UnsignedBigInteger const& other) const; - [[nodiscard]] UnsignedBigInteger bitwise_not_fill_to_one_based_index(size_t) const; - [[nodiscard]] UnsignedBigInteger shift_left(size_t num_bits) const; + [[nodiscard]] ErrorOr bitwise_not_fill_to_one_based_index(size_t) const; + [[nodiscard]] ErrorOr shift_left(size_t num_bits) const; [[nodiscard]] UnsignedBigInteger shift_right(size_t num_bits) const; - [[nodiscard]] UnsignedBigInteger as_n_bits(size_t n) const; [[nodiscard]] UnsignedBigInteger multiplied_by(UnsignedBigInteger const& other) const; [[nodiscard]] UnsignedDivisionResult divided_by(UnsignedBigInteger const& divisor) const; [[nodiscard]] UnsignedBigInteger pow(u32 exponent) const; [[nodiscard]] UnsignedBigInteger gcd(UnsignedBigInteger const& other) const; - [[nodiscard]] ErrorOr try_bitwise_not_fill_to_one_based_index(size_t) const; - [[nodiscard]] ErrorOr try_shift_left(size_t num_bits) const; - [[nodiscard]] u32 hash() const; - void set_bit_inplace(size_t bit_index); - [[nodiscard]] bool operator==(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator!=(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator<(UnsignedBigInteger const& other) const; @@ -135,19 +99,10 @@ public: [[nodiscard]] CompareResult compare_to_double(double) const; private: - friend class UnsignedBigIntegerAlgorithms; + friend class SignedBigInteger; - // Little endian - // m_word[0] + m_word[1] * Word::MAX + m_word[2] * Word::MAX * Word::MAX + ... - Vector m_words; - StorageSpan words_span() { return { m_words.data(), m_words.size() }; } - ConstStorageSpan words_span() const - { - return { m_words.data(), m_words.size() }; - } - - mutable u32 m_cached_hash { 0 }; - mutable Optional m_cached_trimmed_length; + mp_int m_mp; + mutable Optional m_hash {}; }; struct UnsignedDivisionResult { diff --git a/Libraries/LibCrypto/CMakeLists.txt b/Libraries/LibCrypto/CMakeLists.txt index fea1daf2e40..086b7c0aaca 100644 --- a/Libraries/LibCrypto/CMakeLists.txt +++ b/Libraries/LibCrypto/CMakeLists.txt @@ -7,13 +7,6 @@ set(SOURCES ASN1/PEM.cpp Authentication/HMAC.cpp BigFraction/BigFraction.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 Certificate/Certificate.cpp diff --git a/Libraries/LibCrypto/PK/RSA.h b/Libraries/LibCrypto/PK/RSA.h index 8a61c7f86a0..7793e0c2cde 100644 --- a/Libraries/LibCrypto/PK/RSA.h +++ b/Libraries/LibCrypto/PK/RSA.h @@ -21,7 +21,7 @@ public: RSAPublicKey(Integer n, Integer e) : m_modulus(move(n)) , m_public_exponent(move(e)) - , m_length(m_modulus.trimmed_length() * sizeof(u32)) + , m_length(m_modulus.byte_length()) { } @@ -52,7 +52,7 @@ public: { m_modulus = move(n); m_public_exponent = move(e); - m_length = (m_modulus.trimmed_length() * sizeof(u32)); + m_length = m_modulus.byte_length(); } private: @@ -68,7 +68,7 @@ public: : m_modulus(move(n)) , m_private_exponent(move(d)) , m_public_exponent(move(e)) - , m_length(m_modulus.trimmed_length() * sizeof(u32)) + , m_length(m_modulus.byte_length()) { } @@ -81,7 +81,7 @@ public: , m_exponent_1(move(dp)) , m_exponent_2(move(dq)) , m_coefficient(move(qinv)) - , m_length(m_modulus.trimmed_length() * sizeof(u32)) + , m_length(m_modulus.byte_length()) { } diff --git a/Libraries/LibJS/Runtime/BigIntConstructor.cpp b/Libraries/LibJS/Runtime/BigIntConstructor.cpp index 5ebea4b6873..43f204cace6 100644 --- a/Libraries/LibJS/Runtime/BigIntConstructor.cpp +++ b/Libraries/LibJS/Runtime/BigIntConstructor.cpp @@ -17,9 +17,6 @@ namespace JS { GC_DEFINE_ALLOCATOR(BigIntConstructor); -static Crypto::SignedBigInteger const BIGINT_ONE { 1 }; -static Crypto::SignedBigInteger const BIGINT_ZERO { 0 }; - BigIntConstructor::BigIntConstructor(Realm& realm) : NativeFunction(realm.vm().names.BigInt.as_string(), realm.intrinsics().function_prototype()) { @@ -75,19 +72,19 @@ JS_DEFINE_NATIVE_FUNCTION(BigIntConstructor::as_int_n) // OPTIMIZATION: mod = bigint (mod 2^0) = 0 < 2^(0-1) = 0.5 if (bits == 0) - return BigInt::create(vm, BIGINT_ZERO); + return BigInt::create(vm, 0); // 3. Let mod be ℝ(bigint) modulo 2^bits. auto const mod = TRY_OR_THROW_OOM(vm, bigint->big_integer().mod_power_of_two(bits)); // OPTIMIZATION: mod < 2^(bits-1) if (mod.is_zero()) - return BigInt::create(vm, BIGINT_ZERO); + return BigInt::create(vm, 0); // 4. If mod ≥ 2^(bits-1), return ℤ(mod - 2^bits); ... if (auto top_bit_index = mod.unsigned_value().one_based_index_of_highest_set_bit(); top_bit_index >= bits) { // twos complement decode - auto decoded = TRY_OR_THROW_OOM(vm, mod.unsigned_value().try_bitwise_not_fill_to_one_based_index(bits)).plus(1); + auto decoded = TRY_OR_THROW_OOM(vm, mod.unsigned_value().bitwise_not_fill_to_one_based_index(bits)).plus(1); return BigInt::create(vm, Crypto::SignedBigInteger { std::move(decoded), true }); } diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index 2e3f5be85ee..74205a69986 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -116,10 +116,9 @@ ErrorOr base64_url_uint_encode(::Crypto::UnsignedBigInteger integer) // value. Zero is represented as BASE64URL(single zero-valued // octet), which is "AA". - auto bytes = TRY(ByteBuffer::create_uninitialized(integer.trimmed_byte_length())); + auto bytes = TRY(ByteBuffer::create_uninitialized(integer.byte_length())); - bool const remove_leading_zeroes = true; - auto data_size = integer.export_data(bytes.span(), remove_leading_zeroes); + auto data_size = integer.export_data(bytes.span()); auto data_slice_be = bytes.bytes().slice(bytes.size() - data_size, data_size); @@ -1057,12 +1056,12 @@ WebIDL::ExceptionOr> RSAOAEP::import_key(Web::Crypto::Algorit // 6. Set the publicExponent attribute of algorithm to the BigInteger representation of the RSA public exponent. TRY(key->handle().visit( [&](::Crypto::PK::RSAPublicKey<> const& public_key) -> WebIDL::ExceptionOr { - algorithm->set_modulus_length(public_key.modulus().trimmed_byte_length() * 8); + algorithm->set_modulus_length(public_key.modulus().byte_length() * 8); TRY(algorithm->set_public_exponent(public_key.public_exponent())); return {}; }, [&](::Crypto::PK::RSAPrivateKey<> const& private_key) -> WebIDL::ExceptionOr { - algorithm->set_modulus_length(private_key.modulus().trimmed_byte_length() * 8); + algorithm->set_modulus_length(private_key.modulus().byte_length() * 8); TRY(algorithm->set_public_exponent(private_key.public_exponent())); return {}; }, @@ -1635,12 +1634,12 @@ WebIDL::ExceptionOr> RSAPSS::import_key(AlgorithmParams const // 6. Set the publicExponent attribute of algorithm to the BigInteger representation of the RSA public exponent. TRY(key->handle().visit( [&](::Crypto::PK::RSAPublicKey<> const& public_key) -> WebIDL::ExceptionOr { - algorithm->set_modulus_length(public_key.modulus().trimmed_byte_length() * 8); + algorithm->set_modulus_length(public_key.modulus().byte_length() * 8); TRY(algorithm->set_public_exponent(public_key.public_exponent())); return {}; }, [&](::Crypto::PK::RSAPrivateKey<> const& private_key) -> WebIDL::ExceptionOr { - algorithm->set_modulus_length(private_key.modulus().trimmed_byte_length() * 8); + algorithm->set_modulus_length(private_key.modulus().byte_length() * 8); TRY(algorithm->set_public_exponent(private_key.public_exponent())); return {}; }, @@ -2206,12 +2205,12 @@ WebIDL::ExceptionOr> RSASSAPKCS1::import_key(AlgorithmParams // 6. Set the publicExponent attribute of algorithm to the BigInteger representation of the RSA public exponent. TRY(key->handle().visit( [&](::Crypto::PK::RSAPublicKey<> const& public_key) -> WebIDL::ExceptionOr { - algorithm->set_modulus_length(public_key.modulus().trimmed_byte_length() * 8); + algorithm->set_modulus_length(public_key.modulus().byte_length() * 8); TRY(algorithm->set_public_exponent(public_key.public_exponent())); return {}; }, [&](::Crypto::PK::RSAPrivateKey<> const& private_key) -> WebIDL::ExceptionOr { - algorithm->set_modulus_length(private_key.modulus().trimmed_byte_length() * 8); + algorithm->set_modulus_length(private_key.modulus().byte_length() * 8); TRY(algorithm->set_public_exponent(private_key.public_exponent())); return {}; }, diff --git a/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp b/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp index 4349d61a15d..6992b425e06 100644 --- a/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp +++ b/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp @@ -96,10 +96,9 @@ WebIDL::ExceptionOr RsaKeyAlgorithm::set_public_exponent(::Crypto::Unsigne auto& realm = this->realm(); auto& vm = this->vm(); - auto bytes = TRY_OR_THROW_OOM(vm, ByteBuffer::create_uninitialized(exponent.trimmed_byte_length())); + auto bytes = TRY_OR_THROW_OOM(vm, ByteBuffer::create_uninitialized(exponent.byte_length())); - bool const remove_leading_zeroes = true; - auto data_size = exponent.export_data(bytes.span(), remove_leading_zeroes); + auto data_size = exponent.export_data(bytes.span()); auto data_slice_be = bytes.bytes().slice(bytes.size() - data_size, data_size); // The BigInteger typedef from the WebCrypto spec requires the bytes in the Uint8Array be ordered in Big Endian diff --git a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp index 73c2e8b8a27..57c94f4fac7 100644 --- a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp +++ b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp @@ -507,13 +507,13 @@ JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress add JS::ThrowCompletionOr to_webassembly_value(JS::VM& vm, JS::Value value, Wasm::ValueType const& type) { - static ::Crypto::SignedBigInteger two_64 = "1"_sbigint.shift_left(64); + static ::Crypto::SignedBigInteger two_64 = TRY_OR_THROW_OOM(vm, "1"_sbigint.shift_left(64)); switch (type.kind()) { case Wasm::ValueType::I64: { auto bigint = TRY(value.to_bigint(vm)); auto value = bigint->big_integer().divided_by(two_64).remainder; - VERIFY(value.unsigned_value().trimmed_length() <= 2); + VERIFY(value.unsigned_value().byte_length() <= sizeof(i64)); i64 integer = static_cast(value.unsigned_value().to_u64()); if (value.is_negative()) integer = -integer; diff --git a/Meta/gn/secondary/Userland/Libraries/LibCrypto/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibCrypto/BUILD.gn index dfaa6577daf..1346b71fe7c 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibCrypto/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibCrypto/BUILD.gn @@ -14,13 +14,6 @@ shared_library("LibCrypto") { "Authentication/GHash.cpp", "Authentication/Poly1305.cpp", "BigFraction/BigFraction.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/Tests/LibCrypto/TestASN1.cpp b/Tests/LibCrypto/TestASN1.cpp index 57d8f45de62..1d5abe53728 100644 --- a/Tests/LibCrypto/TestASN1.cpp +++ b/Tests/LibCrypto/TestASN1.cpp @@ -181,8 +181,8 @@ TEST_CASE(test_encoder_primitives) roundtrip_value(Crypto::UnsignedBigInteger { 0 }); roundtrip_value(Crypto::UnsignedBigInteger { 1 }); - roundtrip_value(Crypto::UnsignedBigInteger { 2 }.shift_left(128)); - roundtrip_value(Crypto::UnsignedBigInteger { 2 }.shift_left(256)); + roundtrip_value(TRY_OR_FAIL(Crypto::UnsignedBigInteger { 2 }.shift_left(128))); + roundtrip_value(TRY_OR_FAIL(Crypto::UnsignedBigInteger { 2 }.shift_left(256))); roundtrip_value(Vector { 1, 2, 840, 113549, 1, 1, 1 }); roundtrip_value(Vector { 1, 2, 840, 113549, 1, 1, 11 }); diff --git a/Tests/LibCrypto/TestBigInteger.cpp b/Tests/LibCrypto/TestBigInteger.cpp index 67bb06401b7..169c79af207 100644 --- a/Tests/LibCrypto/TestBigInteger.cpp +++ b/Tests/LibCrypto/TestBigInteger.cpp @@ -6,7 +6,6 @@ */ #include -#include #include #include #include @@ -71,57 +70,6 @@ TEST_CASE(test_unsigned_bigint_addition_borrow_with_zero) EXPECT_EQ(num1.plus(num2).words(), expected_result); } -TEST_CASE(test_unsigned_bigint_basic_add_to_accumulator) -{ - Crypto::UnsignedBigInteger num1(10); - Crypto::UnsignedBigInteger num2(70); - Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); - EXPECT_EQ(num1.words(), Vector { 80 }); -} - -TEST_CASE(test_unsigned_bigint_basic_add_to_empty_accumulator) -{ - Crypto::UnsignedBigInteger num1 {}; - Crypto::UnsignedBigInteger num2(10); - Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); - EXPECT_EQ(num1.words(), Vector { 10 }); -} - -TEST_CASE(test_unsigned_bigint_basic_add_to_smaller_accumulator) -{ - Crypto::UnsignedBigInteger num1(10); - Crypto::UnsignedBigInteger num2({ 10, 10 }); - Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); - Vector expected_result { 20, 10 }; - EXPECT_EQ(num1.words(), expected_result); -} - -TEST_CASE(test_unsigned_bigint_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); - Vector expected_result { 2, 0, 1 }; - EXPECT_EQ(num1.words(), expected_result); -} - -TEST_CASE(test_unsigned_bigint_add_to_accumulator_with_leading_zero) -{ - Crypto::UnsignedBigInteger num1(1); - Crypto::UnsignedBigInteger num2({ 1, 0 }); - Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); - EXPECT_EQ(num1.words(), Vector { 2 }); -} - -TEST_CASE(test_unsigned_bigint_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); - Vector expected_result { 0, 1, 0, 0 }; - EXPECT_EQ(num1.words(), expected_result); -} - TEST_CASE(test_unsigned_bigint_simple_subtraction) { Crypto::UnsignedBigInteger num1(80); @@ -167,14 +115,15 @@ TEST_CASE(test_unsigned_bigint_subtraction_with_large_numbers2) Crypto::UnsignedBigInteger num2(Vector { 4196414175, 1117247942, 1123294122, 191895498, 3347106536, 16 }); ErrorOr result = num1.minus(num2); // this test only verifies that we don't crash on an assertion + (void)result; } TEST_CASE(test_unsigned_bigint_subtraction_regression_1) { - auto num = Crypto::UnsignedBigInteger { 1 }.shift_left(256); + auto num = TRY_OR_FAIL(Crypto::UnsignedBigInteger { 1 }.shift_left(256)); Vector expected_result { 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, - 4294967295, 4294967295, 4294967295, 0 + 4294967295, 4294967295, 4294967295 }; EXPECT_EQ(TRY_OR_FAIL(num.minus(1)).words(), expected_result); } @@ -266,7 +215,7 @@ TEST_CASE(test_bigint_import_big_endian_decode_encode_roundtrip) u8 target_buffer[128]; fill_with_random(random_bytes); auto encoded = Crypto::UnsignedBigInteger::import_data(random_bytes, 128); - encoded.export_data({ target_buffer, 128 }); + (void)encoded.export_data({ target_buffer, 128 }); EXPECT(memcmp(target_buffer, random_bytes, 128) == 0); } @@ -288,16 +237,14 @@ TEST_CASE(test_bigint_big_endian_import) TEST_CASE(test_bigint_big_endian_export) { auto number = "448378203247"_bigint; - char exported[8] { 0 }; - auto exported_length = number.export_data({ exported, 8 }, true); + char exported[8] {}; + auto exported_length = number.export_data({ exported, 8 }); EXPECT_EQ(exported_length, 5u); - EXPECT(memcmp(exported + 3, "hello", 5) == 0); + EXPECT(memcmp(exported, "hello", 5) == 0); } TEST_CASE(test_bigint_one_based_index_of_highest_set_bit) { - auto num1 = "1234567"_bigint; - auto num2 = "1234567"_bigint; EXPECT_EQ("0"_bigint.one_based_index_of_highest_set_bit(), 0u); EXPECT_EQ("1"_bigint.one_based_index_of_highest_set_bit(), 1u); EXPECT_EQ("7"_bigint.one_based_index_of_highest_set_bit(), 3u); @@ -306,12 +253,12 @@ TEST_CASE(test_bigint_one_based_index_of_highest_set_bit) TEST_CASE(test_signed_bigint_bitwise_not_fill_to_one_based_index) { - EXPECT_EQ("0"_bigint.bitwise_not_fill_to_one_based_index(0), "0"_bigint); - EXPECT_EQ("0"_bigint.bitwise_not_fill_to_one_based_index(1), "1"_bigint); - EXPECT_EQ("0"_bigint.bitwise_not_fill_to_one_based_index(2), "3"_bigint); - EXPECT_EQ("0"_bigint.bitwise_not_fill_to_one_based_index(4), "15"_bigint); - EXPECT_EQ("0"_bigint.bitwise_not_fill_to_one_based_index(32), "4294967295"_bigint); - EXPECT_EQ("0"_bigint.bitwise_not_fill_to_one_based_index(33), "8589934591"_bigint); + EXPECT_EQ(TRY_OR_FAIL("0"_bigint.bitwise_not_fill_to_one_based_index(0)), "0"_bigint); + EXPECT_EQ(TRY_OR_FAIL("0"_bigint.bitwise_not_fill_to_one_based_index(1)), "1"_bigint); + EXPECT_EQ(TRY_OR_FAIL("0"_bigint.bitwise_not_fill_to_one_based_index(2)), "3"_bigint); + EXPECT_EQ(TRY_OR_FAIL("0"_bigint.bitwise_not_fill_to_one_based_index(4)), "15"_bigint); + EXPECT_EQ(TRY_OR_FAIL("0"_bigint.bitwise_not_fill_to_one_based_index(32)), "4294967295"_bigint); + EXPECT_EQ(TRY_OR_FAIL("0"_bigint.bitwise_not_fill_to_one_based_index(33)), "8589934591"_bigint); } TEST_CASE(test_bigint_bitwise_or) @@ -415,7 +362,7 @@ TEST_CASE(test_bigint_shift_left) }; for (size_t i = 0; i < tests; ++i) - EXPECT_EQ(num.shift_left(results[i].get<0>()).words(), results[i].get<1>()); + EXPECT_EQ(TRY_OR_FAIL(num.shift_left(results[i].get<0>())).words(), results[i].get<1>()); } TEST_CASE(test_bigint_shift_right) @@ -535,6 +482,7 @@ TEST_CASE(test_signed_subtraction_with_large_numbers_check_for_assertion) Crypto::SignedBigInteger num2(Crypto::UnsignedBigInteger { Vector { 4196414175, 1117247942, 1123294122, 191895498, 3347106536, 16 } }); Crypto::SignedBigInteger result = num1.minus(num2); // this test only verifies that we don't crash on an assertion + (void)result; } TEST_CASE(test_signed_multiplication_with_negative_number)