mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-10 13:00:29 +03:00
LibCrypto: Split BigInteger operations into an Algorithms class
Since the operations are already complicated and will become even more so soon, let's split them into their own files. We can also integrate the NumberTheory operations that would better fit there into this class as well. This commit doesn't change behaviors, but moves the allocation of some variables into caller classes.
This commit is contained in:
parent
0853d98420
commit
5963f6f9ff
Notes:
sideshowbarker
2024-07-18 18:13:52 +09:00
Author: https://github.com/Dexesttp Commit: https://github.com/SerenityOS/serenity/commit/5963f6f9ffa Pull-request: https://github.com/SerenityOS/serenity/pull/7067 Reviewed-by: https://github.com/alimpfard
@ -69,6 +69,7 @@ file(GLOB LIBCOMPRESS_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCom
|
|||||||
file(GLOB LIBCOMPRESS_TESTS CONFIGURE_DEPENDS "../../Tests/LibCompress/*.cpp")
|
file(GLOB LIBCOMPRESS_TESTS CONFIGURE_DEPENDS "../../Tests/LibCompress/*.cpp")
|
||||||
file(GLOB LIBCRYPTO_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCrypto/*.cpp")
|
file(GLOB LIBCRYPTO_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCrypto/*.cpp")
|
||||||
file(GLOB LIBCRYPTO_SUBDIR_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCrypto/*/*.cpp")
|
file(GLOB LIBCRYPTO_SUBDIR_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCrypto/*/*.cpp")
|
||||||
|
file(GLOB LIBCRYPTO_SUBSUBDIR_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCrypto/*/*/*.cpp")
|
||||||
file(GLOB LIBTLS_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTLS/*.cpp")
|
file(GLOB LIBTLS_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTLS/*.cpp")
|
||||||
file(GLOB LIBTTF_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTTF/*.cpp")
|
file(GLOB LIBTTF_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTTF/*.cpp")
|
||||||
file(GLOB LIBTEXTCODEC_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTextCodec/*.cpp")
|
file(GLOB LIBTEXTCODEC_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTextCodec/*.cpp")
|
||||||
@ -85,7 +86,7 @@ file(GLOB LIBTEST_MAIN CONFIGURE_DEPENDS "../../Userland/Libraries/LibTest/TestM
|
|||||||
|
|
||||||
set(LAGOM_REGEX_SOURCES ${LIBREGEX_LIBC_SOURCES} ${LIBREGEX_SOURCES})
|
set(LAGOM_REGEX_SOURCES ${LIBREGEX_LIBC_SOURCES} ${LIBREGEX_SOURCES})
|
||||||
set(LAGOM_CORE_SOURCES ${AK_SOURCES} ${LIBCORE_SOURCES})
|
set(LAGOM_CORE_SOURCES ${AK_SOURCES} ${LIBCORE_SOURCES})
|
||||||
set(LAGOM_MORE_SOURCES ${LIBARCHIVE_SOURCES} ${LIBAUDIO_SOURCES} ${LIBELF_SOURCES} ${LIBIPC_SOURCES} ${LIBLINE_SOURCES} ${LIBJS_SOURCES} ${LIBJS_SUBDIR_SOURCES} ${LIBX86_SOURCES} ${LIBCRYPTO_SOURCES} ${LIBCOMPRESS_SOURCES} ${LIBCRYPTO_SUBDIR_SOURCES} ${LIBTLS_SOURCES} ${LIBTTF_SOURCES} ${LIBTEXTCODEC_SOURCES} ${LIBMARKDOWN_SOURCES} ${LIBGEMINI_SOURCES} ${LIBGFX_SOURCES} ${LIBGUI_GML_SOURCES} ${LIBHTTP_SOURCES} ${LAGOM_REGEX_SOURCES} ${SHELL_SOURCES} ${LIBSQL_SOURCES})
|
set(LAGOM_MORE_SOURCES ${LIBARCHIVE_SOURCES} ${LIBAUDIO_SOURCES} ${LIBELF_SOURCES} ${LIBIPC_SOURCES} ${LIBLINE_SOURCES} ${LIBJS_SOURCES} ${LIBJS_SUBDIR_SOURCES} ${LIBX86_SOURCES} ${LIBCRYPTO_SOURCES} ${LIBCOMPRESS_SOURCES} ${LIBCRYPTO_SUBDIR_SOURCES} ${LIBCRYPTO_SUBSUBDIR_SOURCES} ${LIBTLS_SOURCES} ${LIBTTF_SOURCES} ${LIBTEXTCODEC_SOURCES} ${LIBMARKDOWN_SOURCES} ${LIBGEMINI_SOURCES} ${LIBGFX_SOURCES} ${LIBGUI_GML_SOURCES} ${LIBHTTP_SOURCES} ${LAGOM_REGEX_SOURCES} ${SHELL_SOURCES} ${LIBSQL_SOURCES})
|
||||||
set(LAGOM_TEST_SOURCES ${LIBTEST_SOURCES})
|
set(LAGOM_TEST_SOURCES ${LIBTEST_SOURCES})
|
||||||
|
|
||||||
# FIXME: This is a hack, because the lagom stuff can be build individually or
|
# FIXME: This is a hack, because the lagom stuff can be build individually or
|
||||||
|
@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||||
|
* Copyright (c) 2020-2021, Dex♪ <dexes.ttp@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UnsignedBigIntegerAlgorithms.h"
|
||||||
|
|
||||||
|
namespace Crypto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complexity: O(N) where N is the number of words in the shorter value
|
||||||
|
* Method:
|
||||||
|
* Apply <op> word-wise until words in the shorter value are used up
|
||||||
|
* then copy the rest of the words verbatim from the longer value.
|
||||||
|
*/
|
||||||
|
FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_or_without_allocation(
|
||||||
|
UnsignedBigInteger const& left,
|
||||||
|
UnsignedBigInteger const& right,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
// If either of the BigInts are invalid, the output is just the other one.
|
||||||
|
if (left.is_invalid()) {
|
||||||
|
output.set_to(right);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (right.is_invalid()) {
|
||||||
|
output.set_to(left);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnsignedBigInteger *shorter, *longer;
|
||||||
|
if (left.length() < right.length()) {
|
||||||
|
shorter = &left;
|
||||||
|
longer = &right;
|
||||||
|
} else {
|
||||||
|
shorter = &right;
|
||||||
|
longer = &left;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.m_words.resize_and_keep_capacity(longer->length());
|
||||||
|
|
||||||
|
size_t longer_offset = longer->length() - shorter->length();
|
||||||
|
for (size_t i = 0; i < shorter->length(); ++i)
|
||||||
|
output.m_words[i] = longer->words()[i] | shorter->words()[i];
|
||||||
|
|
||||||
|
__builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complexity: O(N) where N is the number of words in the shorter value
|
||||||
|
* Method:
|
||||||
|
* Apply 'and' word-wise until words in the shorter value are used up
|
||||||
|
* and zero the rest.
|
||||||
|
*/
|
||||||
|
FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_and_without_allocation(
|
||||||
|
UnsignedBigInteger const& left,
|
||||||
|
UnsignedBigInteger const& right,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
// If either of the BigInts are invalid, the output is just the other one.
|
||||||
|
if (left.is_invalid()) {
|
||||||
|
output.set_to(right);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (right.is_invalid()) {
|
||||||
|
output.set_to(left);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnsignedBigInteger *shorter, *longer;
|
||||||
|
if (left.length() < right.length()) {
|
||||||
|
shorter = &left;
|
||||||
|
longer = &right;
|
||||||
|
} else {
|
||||||
|
shorter = &right;
|
||||||
|
longer = &left;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.m_words.resize_and_keep_capacity(longer->length());
|
||||||
|
|
||||||
|
size_t longer_offset = longer->length() - shorter->length();
|
||||||
|
for (size_t i = 0; i < shorter->length(); ++i)
|
||||||
|
output.m_words[i] = longer->words()[i] & shorter->words()[i];
|
||||||
|
|
||||||
|
__builtin_memset(output.m_words.data() + shorter->length(), 0, sizeof(u32) * longer_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complexity: O(N) where N is the number of words in the shorter value
|
||||||
|
* Method:
|
||||||
|
* Apply 'xor' word-wise until words in the shorter value are used up
|
||||||
|
* and copy the rest.
|
||||||
|
*/
|
||||||
|
FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_xor_without_allocation(
|
||||||
|
UnsignedBigInteger const& left,
|
||||||
|
UnsignedBigInteger const& right,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
// If either of the BigInts are invalid, the output is just the other one.
|
||||||
|
if (left.is_invalid()) {
|
||||||
|
output.set_to(right);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (right.is_invalid()) {
|
||||||
|
output.set_to(left);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnsignedBigInteger *shorter, *longer;
|
||||||
|
if (left.length() < right.length()) {
|
||||||
|
shorter = &left;
|
||||||
|
longer = &right;
|
||||||
|
} else {
|
||||||
|
shorter = &right;
|
||||||
|
longer = &left;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.m_words.resize_and_keep_capacity(longer->length());
|
||||||
|
|
||||||
|
size_t longer_offset = longer->length() - shorter->length();
|
||||||
|
for (size_t i = 0; i < shorter->length(); ++i)
|
||||||
|
output.m_words[i] = longer->words()[i] ^ shorter->words()[i];
|
||||||
|
|
||||||
|
__builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complexity: O(N) where N is the number of words
|
||||||
|
*/
|
||||||
|
FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_not_without_allocation(
|
||||||
|
UnsignedBigInteger const& right,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
// If the value is invalid, the output value is invalid as well.
|
||||||
|
if (right.is_invalid()) {
|
||||||
|
output.invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (right.length() == 0) {
|
||||||
|
output.set_to_0();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.m_words.resize_and_keep_capacity(right.length());
|
||||||
|
|
||||||
|
if (right.length() > 1) {
|
||||||
|
for (size_t i = 0; i < right.length() - 1; ++i)
|
||||||
|
output.m_words[i] = ~right.words()[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto last_word_index = right.length() - 1;
|
||||||
|
auto last_word = right.words()[last_word_index];
|
||||||
|
|
||||||
|
output.m_words[last_word_index] = ((u32)0xffffffffffffffff >> __builtin_clz(last_word)) & ~last_word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complexity : O(N + num_bits % 8) where N is the number of words in the number
|
||||||
|
* Shift method :
|
||||||
|
* Start by shifting by whole words in num_bits (by putting missing words at the start),
|
||||||
|
* then shift the number's words two by two by the remaining amount of bits.
|
||||||
|
*/
|
||||||
|
FLATTEN void UnsignedBigIntegerAlgorithms::shift_left_without_allocation(
|
||||||
|
UnsignedBigInteger const& number,
|
||||||
|
size_t num_bits,
|
||||||
|
UnsignedBigInteger& temp_result,
|
||||||
|
UnsignedBigInteger& temp_plus,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
// We can only do shift operations on individual words
|
||||||
|
// where the shift amount is <= size of word (32).
|
||||||
|
// But we do know how to shift by a multiple of word size (e.g 64=32*2)
|
||||||
|
// So we first shift the result by how many whole words fit in 'num_bits'
|
||||||
|
shift_left_by_n_words(number, num_bits / UnsignedBigInteger::BITS_IN_WORD, temp_result);
|
||||||
|
|
||||||
|
output.set_to(temp_result);
|
||||||
|
|
||||||
|
// And now we shift by the leftover amount of bits
|
||||||
|
num_bits %= UnsignedBigInteger::BITS_IN_WORD;
|
||||||
|
|
||||||
|
if (num_bits == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < temp_result.length(); ++i) {
|
||||||
|
u32 current_word_of_temp_result = shift_left_get_one_word(temp_result, num_bits, i);
|
||||||
|
output.m_words[i] = current_word_of_temp_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shifting the last word can produce a carry
|
||||||
|
u32 carry_word = shift_left_get_one_word(temp_result, num_bits, temp_result.length());
|
||||||
|
if (carry_word != 0) {
|
||||||
|
|
||||||
|
// output += (carry_word << temp_result.length())
|
||||||
|
// FIXME : Using temp_plus this way to transform carry_word into a bigint is not
|
||||||
|
// efficient nor pretty. Maybe we should have an "add_with_shift" method ?
|
||||||
|
temp_plus.set_to_0();
|
||||||
|
temp_plus.m_words.append(carry_word);
|
||||||
|
shift_left_by_n_words(temp_plus, temp_result.length(), temp_result);
|
||||||
|
add_without_allocation(output, temp_result, temp_plus);
|
||||||
|
output.set_to(temp_plus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE void UnsignedBigIntegerAlgorithms::shift_left_by_n_words(
|
||||||
|
UnsignedBigInteger const& number,
|
||||||
|
size_t number_of_words,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
// shifting left by N words means just inserting N zeroes to the beginning of the words vector
|
||||||
|
output.set_to_0();
|
||||||
|
output.m_words.resize_and_keep_capacity(number_of_words + number.length());
|
||||||
|
|
||||||
|
__builtin_memset(output.m_words.data(), 0, number_of_words * sizeof(unsigned));
|
||||||
|
__builtin_memcpy(&output.m_words.data()[number_of_words], number.m_words.data(), number.m_words.size() * sizeof(unsigned));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the word at a requested index in the result of a shift operation
|
||||||
|
*/
|
||||||
|
ALWAYS_INLINE u32 UnsignedBigIntegerAlgorithms::shift_left_get_one_word(
|
||||||
|
UnsignedBigInteger const& number,
|
||||||
|
size_t num_bits,
|
||||||
|
size_t result_word_index)
|
||||||
|
{
|
||||||
|
// "<= length()" (rather than length() - 1) is intentional,
|
||||||
|
// The result inedx of length() is used when calculating the carry word
|
||||||
|
VERIFY(result_word_index <= number.length());
|
||||||
|
VERIFY(num_bits <= UnsignedBigInteger::BITS_IN_WORD);
|
||||||
|
u32 result = 0;
|
||||||
|
|
||||||
|
// we need to check for "num_bits != 0" since shifting right by 32 is apparently undefined behaviour!
|
||||||
|
if (result_word_index > 0 && num_bits != 0) {
|
||||||
|
result += number.m_words[result_word_index - 1] >> (UnsignedBigInteger::BITS_IN_WORD - num_bits);
|
||||||
|
}
|
||||||
|
if (result_word_index < number.length() && num_bits < 32) {
|
||||||
|
result += number.m_words[result_word_index] << num_bits;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
Userland/Libraries/LibCrypto/BigInt/Algorithms/Division.cpp
Normal file
83
Userland/Libraries/LibCrypto/BigInt/Algorithms/Division.cpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||||
|
* Copyright (c) 2020-2021, Dex♪ <dexes.ttp@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UnsignedBigIntegerAlgorithms.h"
|
||||||
|
|
||||||
|
namespace Crypto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complexity: O(N^2) where N is the number of words in the larger number
|
||||||
|
* Division method:
|
||||||
|
* We loop over the bits of the divisor, attempting to subtract divisor<<i from the dividend.
|
||||||
|
* If the result is non-negative, it means that divisor*2^i "fits" in the dividend,
|
||||||
|
* so we set the ith bit in the quotient and reduce divisor<<i from the dividend.
|
||||||
|
* When we're done, what's left from the dividend is the remainder.
|
||||||
|
*/
|
||||||
|
FLATTEN void UnsignedBigIntegerAlgorithms::divide_without_allocation(
|
||||||
|
UnsignedBigInteger const& numerator,
|
||||||
|
UnsignedBigInteger const& denominator,
|
||||||
|
UnsignedBigInteger& temp_shift_result,
|
||||||
|
UnsignedBigInteger& temp_shift_plus,
|
||||||
|
UnsignedBigInteger& temp_shift,
|
||||||
|
UnsignedBigInteger& temp_minus,
|
||||||
|
UnsignedBigInteger& quotient,
|
||||||
|
UnsignedBigInteger& remainder)
|
||||||
|
{
|
||||||
|
quotient.set_to_0();
|
||||||
|
remainder.set_to(numerator);
|
||||||
|
|
||||||
|
// iterate all bits
|
||||||
|
for (int word_index = numerator.trimmed_length() - 1; word_index >= 0; --word_index) {
|
||||||
|
for (int bit_index = UnsignedBigInteger::BITS_IN_WORD - 1; bit_index >= 0; --bit_index) {
|
||||||
|
size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index;
|
||||||
|
shift_left_without_allocation(denominator, shift_amount, temp_shift_result, temp_shift_plus, temp_shift);
|
||||||
|
|
||||||
|
subtract_without_allocation(remainder, temp_shift, temp_minus);
|
||||||
|
if (!temp_minus.is_invalid()) {
|
||||||
|
remainder.set_to(temp_minus);
|
||||||
|
quotient.set_bit_inplace(shift_amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complexity : O(N) where N is the number of digits in the numerator
|
||||||
|
* Division method :
|
||||||
|
* Starting from the most significant one, for each half-word of the numerator, combine it
|
||||||
|
* with the existing remainder if any, divide the combined number as a u32 operation and
|
||||||
|
* update the quotient / remainder as needed.
|
||||||
|
*/
|
||||||
|
FLATTEN void UnsignedBigIntegerAlgorithms::divide_u16_without_allocation(
|
||||||
|
UnsignedBigInteger const& numerator,
|
||||||
|
u32 denominator,
|
||||||
|
UnsignedBigInteger& quotient,
|
||||||
|
UnsignedBigInteger& remainder)
|
||||||
|
{
|
||||||
|
VERIFY(denominator < (1 << 16));
|
||||||
|
u32 remainder_word = 0;
|
||||||
|
auto numerator_length = numerator.trimmed_length();
|
||||||
|
quotient.set_to_0();
|
||||||
|
quotient.m_words.resize(numerator_length);
|
||||||
|
for (int word_index = numerator_length - 1; word_index >= 0; --word_index) {
|
||||||
|
auto word_high = numerator.m_words[word_index] >> 16;
|
||||||
|
auto word_low = numerator.m_words[word_index] & ((1 << 16) - 1);
|
||||||
|
|
||||||
|
auto number_to_divide_high = (remainder_word << 16) | word_high;
|
||||||
|
auto quotient_high = number_to_divide_high / denominator;
|
||||||
|
remainder_word = number_to_divide_high % denominator;
|
||||||
|
|
||||||
|
auto number_to_divide_low = remainder_word << 16 | word_low;
|
||||||
|
auto quotient_low = number_to_divide_low / denominator;
|
||||||
|
remainder_word = number_to_divide_low % denominator;
|
||||||
|
|
||||||
|
quotient.m_words[word_index] = (quotient_high << 16) | quotient_low;
|
||||||
|
}
|
||||||
|
remainder.set_to(remainder_word);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
43
Userland/Libraries/LibCrypto/BigInt/Algorithms/GCD.cpp
Normal file
43
Userland/Libraries/LibCrypto/BigInt/Algorithms/GCD.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||||
|
* Copyright (c) 2020-2021, Dex♪ <dexes.ttp@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UnsignedBigIntegerAlgorithms.h"
|
||||||
|
|
||||||
|
namespace Crypto {
|
||||||
|
|
||||||
|
void UnsignedBigIntegerAlgorithms::destructive_GCD_without_allocation(
|
||||||
|
UnsignedBigInteger& temp_a,
|
||||||
|
UnsignedBigInteger& temp_b,
|
||||||
|
UnsignedBigInteger& temp_1,
|
||||||
|
UnsignedBigInteger& temp_2,
|
||||||
|
UnsignedBigInteger& temp_3,
|
||||||
|
UnsignedBigInteger& temp_4,
|
||||||
|
UnsignedBigInteger& temp_quotient,
|
||||||
|
UnsignedBigInteger& temp_remainder,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
if (temp_a == 0) {
|
||||||
|
output.set_to(temp_b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// temp_b %= temp_a
|
||||||
|
divide_without_allocation(temp_b, temp_a, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
||||||
|
temp_b.set_to(temp_remainder);
|
||||||
|
if (temp_b == 0) {
|
||||||
|
output.set_to(temp_a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// temp_a %= temp_b
|
||||||
|
divide_without_allocation(temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
||||||
|
temp_a.set_to(temp_remainder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||||
|
* Copyright (c) 2020-2021, Dex♪ <dexes.ttp@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UnsignedBigIntegerAlgorithms.h"
|
||||||
|
|
||||||
|
namespace Crypto {
|
||||||
|
|
||||||
|
void UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation(
|
||||||
|
UnsignedBigInteger const& a,
|
||||||
|
UnsignedBigInteger const& b,
|
||||||
|
UnsignedBigInteger& temp_1,
|
||||||
|
UnsignedBigInteger& temp_2,
|
||||||
|
UnsignedBigInteger& temp_3,
|
||||||
|
UnsignedBigInteger& temp_4,
|
||||||
|
UnsignedBigInteger& temp_plus,
|
||||||
|
UnsignedBigInteger& temp_minus,
|
||||||
|
UnsignedBigInteger& temp_quotient,
|
||||||
|
UnsignedBigInteger& temp_d,
|
||||||
|
UnsignedBigInteger& temp_u,
|
||||||
|
UnsignedBigInteger& temp_v,
|
||||||
|
UnsignedBigInteger& temp_x,
|
||||||
|
UnsignedBigInteger& result)
|
||||||
|
{
|
||||||
|
UnsignedBigInteger one { 1 };
|
||||||
|
|
||||||
|
temp_u.set_to(a);
|
||||||
|
if (a.words()[0] % 2 == 0) {
|
||||||
|
// u += b
|
||||||
|
add_without_allocation(temp_u, b, temp_plus);
|
||||||
|
temp_u.set_to(temp_plus);
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_v.set_to(b);
|
||||||
|
temp_x.set_to(0);
|
||||||
|
|
||||||
|
// d = b - 1
|
||||||
|
subtract_without_allocation(b, one, temp_d);
|
||||||
|
|
||||||
|
while (!(temp_v == 1)) {
|
||||||
|
while (temp_v < temp_u) {
|
||||||
|
// u -= v
|
||||||
|
subtract_without_allocation(temp_u, temp_v, temp_minus);
|
||||||
|
temp_u.set_to(temp_minus);
|
||||||
|
|
||||||
|
// d += x
|
||||||
|
add_without_allocation(temp_d, temp_x, temp_plus);
|
||||||
|
temp_d.set_to(temp_plus);
|
||||||
|
|
||||||
|
while (temp_u.words()[0] % 2 == 0) {
|
||||||
|
if (temp_d.words()[0] % 2 == 1) {
|
||||||
|
// d += b
|
||||||
|
add_without_allocation(temp_d, b, temp_plus);
|
||||||
|
temp_d.set_to(temp_plus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// u /= 2
|
||||||
|
divide_u16_without_allocation(temp_u, 2, temp_quotient, temp_1);
|
||||||
|
temp_u.set_to(temp_quotient);
|
||||||
|
|
||||||
|
// d /= 2
|
||||||
|
divide_u16_without_allocation(temp_d, 2, temp_quotient, temp_1);
|
||||||
|
temp_d.set_to(temp_quotient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// v -= u
|
||||||
|
subtract_without_allocation(temp_v, temp_u, temp_minus);
|
||||||
|
temp_v.set_to(temp_minus);
|
||||||
|
|
||||||
|
// x += d
|
||||||
|
add_without_allocation(temp_x, temp_d, temp_plus);
|
||||||
|
temp_x.set_to(temp_plus);
|
||||||
|
|
||||||
|
while (temp_v.words()[0] % 2 == 0) {
|
||||||
|
if (temp_x.words()[0] % 2 == 1) {
|
||||||
|
// x += b
|
||||||
|
add_without_allocation(temp_x, b, temp_plus);
|
||||||
|
temp_x.set_to(temp_plus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// v /= 2
|
||||||
|
divide_u16_without_allocation(temp_v, 2, temp_quotient, temp_1);
|
||||||
|
temp_v.set_to(temp_quotient);
|
||||||
|
|
||||||
|
// x /= 2
|
||||||
|
divide_u16_without_allocation(temp_x, 2, temp_quotient, temp_1);
|
||||||
|
temp_x.set_to(temp_quotient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return x % b
|
||||||
|
divide_without_allocation(temp_x, b, temp_1, temp_2, temp_3, temp_4, temp_quotient, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||||
|
* Copyright (c) 2020-2021, Dex♪ <dexes.ttp@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UnsignedBigIntegerAlgorithms.h"
|
||||||
|
|
||||||
|
namespace Crypto {
|
||||||
|
|
||||||
|
void UnsignedBigIntegerAlgorithms::destructive_modular_power_without_allocation(
|
||||||
|
UnsignedBigInteger& ep,
|
||||||
|
UnsignedBigInteger& base,
|
||||||
|
UnsignedBigInteger const& m,
|
||||||
|
UnsignedBigInteger& temp_1,
|
||||||
|
UnsignedBigInteger& temp_2,
|
||||||
|
UnsignedBigInteger& temp_3,
|
||||||
|
UnsignedBigInteger& temp_4,
|
||||||
|
UnsignedBigInteger& temp_multiply,
|
||||||
|
UnsignedBigInteger& temp_quotient,
|
||||||
|
UnsignedBigInteger& temp_remainder,
|
||||||
|
UnsignedBigInteger& exp)
|
||||||
|
{
|
||||||
|
exp.set_to(1);
|
||||||
|
while (!(ep < 1)) {
|
||||||
|
if (ep.words()[0] % 2 == 1) {
|
||||||
|
// exp = (exp * base) % m;
|
||||||
|
multiply_without_allocation(exp, base, temp_1, temp_2, temp_3, temp_4, temp_multiply);
|
||||||
|
divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
||||||
|
exp.set_to(temp_remainder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ep = ep / 2;
|
||||||
|
divide_u16_without_allocation(ep, 2, temp_quotient, temp_remainder);
|
||||||
|
ep.set_to(temp_quotient);
|
||||||
|
|
||||||
|
// base = (base * base) % m;
|
||||||
|
multiply_without_allocation(base, base, temp_1, temp_2, temp_3, temp_4, temp_multiply);
|
||||||
|
divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
||||||
|
base.set_to(temp_remainder);
|
||||||
|
|
||||||
|
// Note that not clamping here would cause future calculations (multiply, specifically) to allocate even more unused space
|
||||||
|
// which would then persist through the temp bigints, and significantly slow down later loops.
|
||||||
|
// To avoid that, we can clamp to a specific max size, or just clamp to the min needed amount of space.
|
||||||
|
ep.clamp_to_trimmed_length();
|
||||||
|
exp.clamp_to_trimmed_length();
|
||||||
|
base.clamp_to_trimmed_length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||||
|
* Copyright (c) 2020-2021, Dex♪ <dexes.ttp@gmail.com>
|
||||||
|
*
|
||||||
|
* 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<<i to the result.
|
||||||
|
*/
|
||||||
|
FLATTEN void UnsignedBigIntegerAlgorithms::multiply_without_allocation(
|
||||||
|
UnsignedBigInteger const& left,
|
||||||
|
UnsignedBigInteger const& right,
|
||||||
|
UnsignedBigInteger& temp_shift_result,
|
||||||
|
UnsignedBigInteger& temp_shift_plus,
|
||||||
|
UnsignedBigInteger& temp_shift,
|
||||||
|
UnsignedBigInteger& temp_plus,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
output.set_to_0();
|
||||||
|
|
||||||
|
// iterate all bits
|
||||||
|
for (size_t word_index = 0; word_index < left.length(); ++word_index) {
|
||||||
|
for (size_t bit_index = 0; bit_index < UnsignedBigInteger::BITS_IN_WORD; ++bit_index) {
|
||||||
|
// If the bit is off - skip over it
|
||||||
|
if (!(left.m_words[word_index] & (1 << bit_index)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index;
|
||||||
|
|
||||||
|
// output += (right << shift_amount);
|
||||||
|
shift_left_without_allocation(right, shift_amount, temp_shift_result, temp_shift_plus, temp_shift);
|
||||||
|
add_without_allocation(output, temp_shift, temp_plus);
|
||||||
|
output.set_to(temp_plus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||||
|
* Copyright (c) 2020-2021, Dex♪ <dexes.ttp@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UnsignedBigIntegerAlgorithms.h"
|
||||||
|
|
||||||
|
namespace Crypto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complexity: O(N) where N is the number of words in the larger number
|
||||||
|
*/
|
||||||
|
void UnsignedBigIntegerAlgorithms::add_without_allocation(
|
||||||
|
UnsignedBigInteger const& left,
|
||||||
|
UnsignedBigInteger const& right,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
const UnsignedBigInteger* const longer = (left.length() > right.length()) ? &left : &right;
|
||||||
|
const UnsignedBigInteger* const shorter = (longer == &right) ? &left : &right;
|
||||||
|
|
||||||
|
u8 carry = 0;
|
||||||
|
|
||||||
|
output.set_to_0();
|
||||||
|
output.m_words.resize_and_keep_capacity(longer->length());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < shorter->length(); ++i) {
|
||||||
|
u32 word_addition_result = shorter->m_words[i] + longer->m_words[i];
|
||||||
|
u8 carry_out = 0;
|
||||||
|
// if there was a carry, the result will be smaller than any of the operands
|
||||||
|
if (word_addition_result + carry < shorter->m_words[i]) {
|
||||||
|
carry_out = 1;
|
||||||
|
}
|
||||||
|
if (carry) {
|
||||||
|
word_addition_result++;
|
||||||
|
}
|
||||||
|
carry = carry_out;
|
||||||
|
output.m_words[i] = word_addition_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = shorter->length(); i < longer->length(); ++i) {
|
||||||
|
u32 word_addition_result = longer->m_words[i] + carry;
|
||||||
|
|
||||||
|
carry = 0;
|
||||||
|
if (word_addition_result < longer->m_words[i]) {
|
||||||
|
carry = 1;
|
||||||
|
}
|
||||||
|
output.m_words[i] = word_addition_result;
|
||||||
|
}
|
||||||
|
if (carry) {
|
||||||
|
output.m_words.append(carry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complexity: O(N) where N is the number of words in the larger number
|
||||||
|
*/
|
||||||
|
void UnsignedBigIntegerAlgorithms::subtract_without_allocation(
|
||||||
|
UnsignedBigInteger const& left,
|
||||||
|
UnsignedBigInteger const& right,
|
||||||
|
UnsignedBigInteger& output)
|
||||||
|
{
|
||||||
|
if (left < right) {
|
||||||
|
output.invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 borrow = 0;
|
||||||
|
auto own_length = left.length();
|
||||||
|
auto other_length = right.length();
|
||||||
|
|
||||||
|
output.set_to_0();
|
||||||
|
output.m_words.resize_and_keep_capacity(own_length);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < own_length; ++i) {
|
||||||
|
u32 other_word = (i < other_length) ? right.m_words[i] : 0;
|
||||||
|
i64 temp = static_cast<i64>(left.m_words[i]) - static_cast<i64>(other_word) - static_cast<i64>(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Dex♪ <dexes.ttp@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||||
|
|
||||||
|
namespace Crypto {
|
||||||
|
|
||||||
|
class UnsignedBigIntegerAlgorithms {
|
||||||
|
public:
|
||||||
|
static void add_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
||||||
|
static void subtract_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
||||||
|
static void bitwise_or_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
||||||
|
static void bitwise_and_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
||||||
|
static void bitwise_xor_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
||||||
|
static void bitwise_not_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger& output);
|
||||||
|
static void shift_left_without_allocation(UnsignedBigInteger const& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
|
||||||
|
static void multiply_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
|
||||||
|
static void divide_without_allocation(UnsignedBigInteger const& numerator, UnsignedBigInteger const& denominator, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_minus, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
|
||||||
|
static void divide_u16_without_allocation(UnsignedBigInteger const& numerator, u32 denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
|
||||||
|
|
||||||
|
static void destructive_GCD_without_allocation(UnsignedBigInteger& temp_a, UnsignedBigInteger& temp_b, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_3, UnsignedBigInteger& temp_4, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_remainder, UnsignedBigInteger& output);
|
||||||
|
static void modular_inverse_without_allocation(UnsignedBigInteger const& a_, UnsignedBigInteger const& b, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_3, UnsignedBigInteger& temp_4, UnsignedBigInteger& temp_plus, UnsignedBigInteger& temp_minus, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_d, UnsignedBigInteger& temp_u, UnsignedBigInteger& temp_v, UnsignedBigInteger& temp_x, UnsignedBigInteger& result);
|
||||||
|
static void destructive_modular_power_without_allocation(UnsignedBigInteger& ep, UnsignedBigInteger& base, UnsignedBigInteger const& m, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_3, UnsignedBigInteger& temp_4, UnsignedBigInteger& temp_multiply, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_remainder, UnsignedBigInteger& result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ALWAYS_INLINE static void shift_left_by_n_words(UnsignedBigInteger const& number, size_t number_of_words, UnsignedBigInteger& output);
|
||||||
|
ALWAYS_INLINE static u32 shift_left_get_one_word(UnsignedBigInteger const& number, size_t num_bits, size_t result_word_index);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "UnsignedBigInteger.h"
|
#include "UnsignedBigInteger.h"
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
|
#include <LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h>
|
||||||
|
|
||||||
namespace Crypto {
|
namespace Crypto {
|
||||||
|
|
||||||
@ -85,7 +86,7 @@ String UnsignedBigInteger::to_base10() const
|
|||||||
UnsignedBigInteger remainder;
|
UnsignedBigInteger remainder;
|
||||||
|
|
||||||
while (temp != UnsignedBigInteger { 0 }) {
|
while (temp != UnsignedBigInteger { 0 }) {
|
||||||
divide_u16_without_allocation(temp, 10, quotient, remainder);
|
UnsignedBigIntegerAlgorithms::divide_u16_without_allocation(temp, 10, quotient, remainder);
|
||||||
VERIFY(remainder.words()[0] < 10);
|
VERIFY(remainder.words()[0] < 10);
|
||||||
builder.append(static_cast<char>(remainder.words()[0] + '0'));
|
builder.append(static_cast<char>(remainder.words()[0] + '0'));
|
||||||
temp.set_to(quotient);
|
temp.set_to(quotient);
|
||||||
@ -147,7 +148,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::plus(const UnsignedBigInteger& ot
|
|||||||
{
|
{
|
||||||
UnsignedBigInteger result;
|
UnsignedBigInteger result;
|
||||||
|
|
||||||
add_without_allocation(*this, other, result);
|
UnsignedBigIntegerAlgorithms::add_without_allocation(*this, other, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -156,7 +157,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::minus(const UnsignedBigInteger& o
|
|||||||
{
|
{
|
||||||
UnsignedBigInteger result;
|
UnsignedBigInteger result;
|
||||||
|
|
||||||
subtract_without_allocation(*this, other, result);
|
UnsignedBigIntegerAlgorithms::subtract_without_allocation(*this, other, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -165,7 +166,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_or(const UnsignedBigInteg
|
|||||||
{
|
{
|
||||||
UnsignedBigInteger result;
|
UnsignedBigInteger result;
|
||||||
|
|
||||||
bitwise_or_without_allocation(*this, other, result);
|
UnsignedBigIntegerAlgorithms::bitwise_or_without_allocation(*this, other, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -174,7 +175,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_and(const UnsignedBigInte
|
|||||||
{
|
{
|
||||||
UnsignedBigInteger result;
|
UnsignedBigInteger result;
|
||||||
|
|
||||||
bitwise_and_without_allocation(*this, other, result);
|
UnsignedBigIntegerAlgorithms::bitwise_and_without_allocation(*this, other, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -183,7 +184,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_xor(const UnsignedBigInte
|
|||||||
{
|
{
|
||||||
UnsignedBigInteger result;
|
UnsignedBigInteger result;
|
||||||
|
|
||||||
bitwise_xor_without_allocation(*this, other, result);
|
UnsignedBigIntegerAlgorithms::bitwise_xor_without_allocation(*this, other, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -192,7 +193,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_not() const
|
|||||||
{
|
{
|
||||||
UnsignedBigInteger result;
|
UnsignedBigInteger result;
|
||||||
|
|
||||||
bitwise_not_without_allocation(*this, result);
|
UnsignedBigIntegerAlgorithms::bitwise_not_without_allocation(*this, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -203,7 +204,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::shift_left(size_t num_bits) const
|
|||||||
UnsignedBigInteger temp_result;
|
UnsignedBigInteger temp_result;
|
||||||
UnsignedBigInteger temp_plus;
|
UnsignedBigInteger temp_plus;
|
||||||
|
|
||||||
shift_left_without_allocation(*this, num_bits, temp_result, temp_plus, output);
|
UnsignedBigIntegerAlgorithms::shift_left_without_allocation(*this, num_bits, temp_result, temp_plus, output);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -216,7 +217,7 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::multiplied_by(const UnsignedBigIn
|
|||||||
UnsignedBigInteger temp_shift;
|
UnsignedBigInteger temp_shift;
|
||||||
UnsignedBigInteger temp_plus;
|
UnsignedBigInteger temp_plus;
|
||||||
|
|
||||||
multiply_without_allocation(*this, other, temp_shift_result, temp_shift_plus, temp_shift, temp_plus, result);
|
UnsignedBigIntegerAlgorithms::multiply_without_allocation(*this, other, temp_shift_result, temp_shift_plus, temp_shift, temp_plus, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -229,7 +230,7 @@ FLATTEN UnsignedDivisionResult UnsignedBigInteger::divided_by(const UnsignedBigI
|
|||||||
// If we actually have a u16-compatible divisor, short-circuit to the
|
// If we actually have a u16-compatible divisor, short-circuit to the
|
||||||
// less computationally-intensive "divide_u16_without_allocation" method.
|
// less computationally-intensive "divide_u16_without_allocation" method.
|
||||||
if (divisor.trimmed_length() == 1 && divisor.m_words[0] < (1 << 16)) {
|
if (divisor.trimmed_length() == 1 && divisor.m_words[0] < (1 << 16)) {
|
||||||
divide_u16_without_allocation(*this, divisor.m_words[0], quotient, remainder);
|
UnsignedBigIntegerAlgorithms::divide_u16_without_allocation(*this, divisor.m_words[0], quotient, remainder);
|
||||||
return UnsignedDivisionResult { quotient, remainder };
|
return UnsignedDivisionResult { quotient, remainder };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +239,7 @@ FLATTEN UnsignedDivisionResult UnsignedBigInteger::divided_by(const UnsignedBigI
|
|||||||
UnsignedBigInteger temp_shift;
|
UnsignedBigInteger temp_shift;
|
||||||
UnsignedBigInteger temp_minus;
|
UnsignedBigInteger temp_minus;
|
||||||
|
|
||||||
divide_without_allocation(*this, divisor, temp_shift_result, temp_shift_plus, temp_shift, temp_minus, quotient, remainder);
|
UnsignedBigIntegerAlgorithms::divide_without_allocation(*this, divisor, temp_shift_result, temp_shift_plus, temp_shift, temp_minus, quotient, remainder);
|
||||||
|
|
||||||
return UnsignedDivisionResult { quotient, remainder };
|
return UnsignedDivisionResult { quotient, remainder };
|
||||||
}
|
}
|
||||||
@ -300,423 +301,6 @@ bool UnsignedBigInteger::operator<(const UnsignedBigInteger& other) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity: O(N) where N is the number of words in the larger number
|
|
||||||
*/
|
|
||||||
void UnsignedBigInteger::add_without_allocation(
|
|
||||||
const UnsignedBigInteger& left,
|
|
||||||
const UnsignedBigInteger& right,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
const UnsignedBigInteger* const longer = (left.length() > right.length()) ? &left : &right;
|
|
||||||
const UnsignedBigInteger* const shorter = (longer == &right) ? &left : &right;
|
|
||||||
|
|
||||||
u8 carry = 0;
|
|
||||||
|
|
||||||
output.set_to_0();
|
|
||||||
output.m_words.resize_and_keep_capacity(longer->length());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < shorter->length(); ++i) {
|
|
||||||
u32 word_addition_result = shorter->m_words[i] + longer->m_words[i];
|
|
||||||
u8 carry_out = 0;
|
|
||||||
// if there was a carry, the result will be smaller than any of the operands
|
|
||||||
if (word_addition_result + carry < shorter->m_words[i]) {
|
|
||||||
carry_out = 1;
|
|
||||||
}
|
|
||||||
if (carry) {
|
|
||||||
word_addition_result++;
|
|
||||||
}
|
|
||||||
carry = carry_out;
|
|
||||||
output.m_words[i] = word_addition_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = shorter->length(); i < longer->length(); ++i) {
|
|
||||||
u32 word_addition_result = longer->m_words[i] + carry;
|
|
||||||
|
|
||||||
carry = 0;
|
|
||||||
if (word_addition_result < longer->m_words[i]) {
|
|
||||||
carry = 1;
|
|
||||||
}
|
|
||||||
output.m_words[i] = word_addition_result;
|
|
||||||
}
|
|
||||||
if (carry) {
|
|
||||||
output.m_words.append(carry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity: O(N) where N is the number of words in the larger number
|
|
||||||
*/
|
|
||||||
void UnsignedBigInteger::subtract_without_allocation(
|
|
||||||
const UnsignedBigInteger& left,
|
|
||||||
const UnsignedBigInteger& right,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
if (left < right) {
|
|
||||||
output.invalidate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 borrow = 0;
|
|
||||||
auto own_length = left.length();
|
|
||||||
auto other_length = right.length();
|
|
||||||
|
|
||||||
output.set_to_0();
|
|
||||||
output.m_words.resize_and_keep_capacity(own_length);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < own_length; ++i) {
|
|
||||||
u32 other_word = (i < other_length) ? right.m_words[i] : 0;
|
|
||||||
i64 temp = static_cast<i64>(left.m_words[i]) - static_cast<i64>(other_word) - static_cast<i64>(borrow);
|
|
||||||
// If temp < 0, we had an underflow
|
|
||||||
borrow = (temp >= 0) ? 0 : 1;
|
|
||||||
if (temp < 0) {
|
|
||||||
temp += (UINT32_MAX + 1);
|
|
||||||
}
|
|
||||||
output.m_words[i] = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This assertion should not fail, because we verified that *this>=other at the beginning of the function
|
|
||||||
VERIFY(borrow == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity: O(N) where N is the number of words in the shorter value
|
|
||||||
* Method:
|
|
||||||
* Apply <op> word-wise until words in the shorter value are used up
|
|
||||||
* then copy the rest of the words verbatim from the longer value.
|
|
||||||
*/
|
|
||||||
FLATTEN void UnsignedBigInteger::bitwise_or_without_allocation(
|
|
||||||
const UnsignedBigInteger& left,
|
|
||||||
const UnsignedBigInteger& right,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
// If either of the BigInts are invalid, the output is just the other one.
|
|
||||||
if (left.is_invalid()) {
|
|
||||||
output.set_to(right);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (right.is_invalid()) {
|
|
||||||
output.set_to(left);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UnsignedBigInteger *shorter, *longer;
|
|
||||||
if (left.length() < right.length()) {
|
|
||||||
shorter = &left;
|
|
||||||
longer = &right;
|
|
||||||
} else {
|
|
||||||
shorter = &right;
|
|
||||||
longer = &left;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.m_words.resize_and_keep_capacity(longer->length());
|
|
||||||
|
|
||||||
size_t longer_offset = longer->length() - shorter->length();
|
|
||||||
for (size_t i = 0; i < shorter->length(); ++i)
|
|
||||||
output.m_words[i] = longer->words()[i] | shorter->words()[i];
|
|
||||||
|
|
||||||
__builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity: O(N) where N is the number of words in the shorter value
|
|
||||||
* Method:
|
|
||||||
* Apply 'and' word-wise until words in the shorter value are used up
|
|
||||||
* and zero the rest.
|
|
||||||
*/
|
|
||||||
FLATTEN void UnsignedBigInteger::bitwise_and_without_allocation(
|
|
||||||
const UnsignedBigInteger& left,
|
|
||||||
const UnsignedBigInteger& right,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
// If either of the BigInts are invalid, the output is just the other one.
|
|
||||||
if (left.is_invalid()) {
|
|
||||||
output.set_to(right);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (right.is_invalid()) {
|
|
||||||
output.set_to(left);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UnsignedBigInteger *shorter, *longer;
|
|
||||||
if (left.length() < right.length()) {
|
|
||||||
shorter = &left;
|
|
||||||
longer = &right;
|
|
||||||
} else {
|
|
||||||
shorter = &right;
|
|
||||||
longer = &left;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.m_words.resize_and_keep_capacity(longer->length());
|
|
||||||
|
|
||||||
size_t longer_offset = longer->length() - shorter->length();
|
|
||||||
for (size_t i = 0; i < shorter->length(); ++i)
|
|
||||||
output.m_words[i] = longer->words()[i] & shorter->words()[i];
|
|
||||||
|
|
||||||
__builtin_memset(output.m_words.data() + shorter->length(), 0, sizeof(u32) * longer_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity: O(N) where N is the number of words in the shorter value
|
|
||||||
* Method:
|
|
||||||
* Apply 'xor' word-wise until words in the shorter value are used up
|
|
||||||
* and copy the rest.
|
|
||||||
*/
|
|
||||||
FLATTEN void UnsignedBigInteger::bitwise_xor_without_allocation(
|
|
||||||
const UnsignedBigInteger& left,
|
|
||||||
const UnsignedBigInteger& right,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
// If either of the BigInts are invalid, the output is just the other one.
|
|
||||||
if (left.is_invalid()) {
|
|
||||||
output.set_to(right);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (right.is_invalid()) {
|
|
||||||
output.set_to(left);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UnsignedBigInteger *shorter, *longer;
|
|
||||||
if (left.length() < right.length()) {
|
|
||||||
shorter = &left;
|
|
||||||
longer = &right;
|
|
||||||
} else {
|
|
||||||
shorter = &right;
|
|
||||||
longer = &left;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.m_words.resize_and_keep_capacity(longer->length());
|
|
||||||
|
|
||||||
size_t longer_offset = longer->length() - shorter->length();
|
|
||||||
for (size_t i = 0; i < shorter->length(); ++i)
|
|
||||||
output.m_words[i] = longer->words()[i] ^ shorter->words()[i];
|
|
||||||
|
|
||||||
__builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity: O(N) where N is the number of words
|
|
||||||
*/
|
|
||||||
FLATTEN void UnsignedBigInteger::bitwise_not_without_allocation(
|
|
||||||
const UnsignedBigInteger& right,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
// If the value is invalid, the output value is invalid as well.
|
|
||||||
if (right.is_invalid()) {
|
|
||||||
output.invalidate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (right.length() == 0) {
|
|
||||||
output.set_to_0();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.m_words.resize_and_keep_capacity(right.length());
|
|
||||||
|
|
||||||
if (right.length() > 1) {
|
|
||||||
for (size_t i = 0; i < right.length() - 1; ++i)
|
|
||||||
output.m_words[i] = ~right.words()[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
auto last_word_index = right.length() - 1;
|
|
||||||
auto last_word = right.words()[last_word_index];
|
|
||||||
|
|
||||||
output.m_words[last_word_index] = ((u32)0xffffffffffffffff >> __builtin_clz(last_word)) & ~last_word;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity : O(N + num_bits % 8) where N is the number of words in the number
|
|
||||||
* Shift method :
|
|
||||||
* Start by shifting by whole words in num_bits (by putting missing words at the start),
|
|
||||||
* then shift the number's words two by two by the remaining amount of bits.
|
|
||||||
*/
|
|
||||||
FLATTEN void UnsignedBigInteger::shift_left_without_allocation(
|
|
||||||
const UnsignedBigInteger& number,
|
|
||||||
size_t num_bits,
|
|
||||||
UnsignedBigInteger& temp_result,
|
|
||||||
UnsignedBigInteger& temp_plus,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
// We can only do shift operations on individual words
|
|
||||||
// where the shift amount is <= size of word (32).
|
|
||||||
// But we do know how to shift by a multiple of word size (e.g 64=32*2)
|
|
||||||
// So we first shift the result by how many whole words fit in 'num_bits'
|
|
||||||
shift_left_by_n_words(number, num_bits / UnsignedBigInteger::BITS_IN_WORD, temp_result);
|
|
||||||
|
|
||||||
output.set_to(temp_result);
|
|
||||||
|
|
||||||
// And now we shift by the leftover amount of bits
|
|
||||||
num_bits %= UnsignedBigInteger::BITS_IN_WORD;
|
|
||||||
|
|
||||||
if (num_bits == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < temp_result.length(); ++i) {
|
|
||||||
u32 current_word_of_temp_result = shift_left_get_one_word(temp_result, num_bits, i);
|
|
||||||
output.m_words[i] = current_word_of_temp_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shifting the last word can produce a carry
|
|
||||||
u32 carry_word = shift_left_get_one_word(temp_result, num_bits, temp_result.length());
|
|
||||||
if (carry_word != 0) {
|
|
||||||
|
|
||||||
// output += (carry_word << temp_result.length())
|
|
||||||
// FIXME : Using temp_plus this way to transform carry_word into a bigint is not
|
|
||||||
// efficient nor pretty. Maybe we should have an "add_with_shift" method ?
|
|
||||||
temp_plus.set_to_0();
|
|
||||||
temp_plus.m_words.append(carry_word);
|
|
||||||
shift_left_by_n_words(temp_plus, temp_result.length(), temp_result);
|
|
||||||
add_without_allocation(output, temp_result, temp_plus);
|
|
||||||
output.set_to(temp_plus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity: O(N^2) where N is the number of words in the larger number
|
|
||||||
* Multiplication method:
|
|
||||||
* An integer is equal to the sum of the powers of two
|
|
||||||
* according to the indices of its 'on' bits.
|
|
||||||
* So to multiple x*y, we go over each '1' bit in x (say the i'th bit),
|
|
||||||
* and add y<<i to the result.
|
|
||||||
*/
|
|
||||||
FLATTEN void UnsignedBigInteger::multiply_without_allocation(
|
|
||||||
const UnsignedBigInteger& left,
|
|
||||||
const UnsignedBigInteger& right,
|
|
||||||
UnsignedBigInteger& temp_shift_result,
|
|
||||||
UnsignedBigInteger& temp_shift_plus,
|
|
||||||
UnsignedBigInteger& temp_shift,
|
|
||||||
UnsignedBigInteger& temp_plus,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
output.set_to_0();
|
|
||||||
|
|
||||||
// iterate all bits
|
|
||||||
for (size_t word_index = 0; word_index < left.length(); ++word_index) {
|
|
||||||
for (size_t bit_index = 0; bit_index < UnsignedBigInteger::BITS_IN_WORD; ++bit_index) {
|
|
||||||
// If the bit is off - skip over it
|
|
||||||
if (!(left.m_words[word_index] & (1 << bit_index)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index;
|
|
||||||
|
|
||||||
// output += (right << shift_amount);
|
|
||||||
shift_left_without_allocation(right, shift_amount, temp_shift_result, temp_shift_plus, temp_shift);
|
|
||||||
add_without_allocation(output, temp_shift, temp_plus);
|
|
||||||
output.set_to(temp_plus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity: O(N^2) where N is the number of words in the larger number
|
|
||||||
* Division method:
|
|
||||||
* We loop over the bits of the divisor, attempting to subtract divisor<<i from the dividend.
|
|
||||||
* If the result is non-negative, it means that divisor*2^i "fits" in the dividend,
|
|
||||||
* so we set the ith bit in the quotient and reduce divisor<<i from the dividend.
|
|
||||||
* When we're done, what's left from the dividend is the remainder.
|
|
||||||
*/
|
|
||||||
FLATTEN void UnsignedBigInteger::divide_without_allocation(
|
|
||||||
const UnsignedBigInteger& numerator,
|
|
||||||
const UnsignedBigInteger& denominator,
|
|
||||||
UnsignedBigInteger& temp_shift_result,
|
|
||||||
UnsignedBigInteger& temp_shift_plus,
|
|
||||||
UnsignedBigInteger& temp_shift,
|
|
||||||
UnsignedBigInteger& temp_minus,
|
|
||||||
UnsignedBigInteger& quotient,
|
|
||||||
UnsignedBigInteger& remainder)
|
|
||||||
{
|
|
||||||
quotient.set_to_0();
|
|
||||||
remainder.set_to(numerator);
|
|
||||||
|
|
||||||
// iterate all bits
|
|
||||||
for (int word_index = numerator.trimmed_length() - 1; word_index >= 0; --word_index) {
|
|
||||||
for (int bit_index = UnsignedBigInteger::BITS_IN_WORD - 1; bit_index >= 0; --bit_index) {
|
|
||||||
const size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index;
|
|
||||||
shift_left_without_allocation(denominator, shift_amount, temp_shift_result, temp_shift_plus, temp_shift);
|
|
||||||
|
|
||||||
subtract_without_allocation(remainder, temp_shift, temp_minus);
|
|
||||||
if (!temp_minus.is_invalid()) {
|
|
||||||
remainder.set_to(temp_minus);
|
|
||||||
quotient.set_bit_inplace(shift_amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complexity : O(N) where N is the number of digits in the numerator
|
|
||||||
* Division method :
|
|
||||||
* Starting from the most significant one, for each half-word of the numerator, combine it
|
|
||||||
* with the existing remainder if any, divide the combined number as a u32 operation and
|
|
||||||
* update the quotient / remainder as needed.
|
|
||||||
*/
|
|
||||||
FLATTEN void UnsignedBigInteger::divide_u16_without_allocation(
|
|
||||||
const UnsignedBigInteger& numerator,
|
|
||||||
u32 denominator,
|
|
||||||
UnsignedBigInteger& quotient,
|
|
||||||
UnsignedBigInteger& remainder)
|
|
||||||
{
|
|
||||||
VERIFY(denominator < (1 << 16));
|
|
||||||
u32 remainder_word = 0;
|
|
||||||
auto numerator_length = numerator.trimmed_length();
|
|
||||||
quotient.set_to_0();
|
|
||||||
quotient.m_words.resize(numerator_length);
|
|
||||||
for (int word_index = numerator_length - 1; word_index >= 0; --word_index) {
|
|
||||||
auto word_high = numerator.m_words[word_index] >> 16;
|
|
||||||
auto word_low = numerator.m_words[word_index] & ((1 << 16) - 1);
|
|
||||||
|
|
||||||
auto number_to_divide_high = (remainder_word << 16) | word_high;
|
|
||||||
auto quotient_high = number_to_divide_high / denominator;
|
|
||||||
remainder_word = number_to_divide_high % denominator;
|
|
||||||
|
|
||||||
auto number_to_divide_low = remainder_word << 16 | word_low;
|
|
||||||
auto quotient_low = number_to_divide_low / denominator;
|
|
||||||
remainder_word = number_to_divide_low % denominator;
|
|
||||||
|
|
||||||
quotient.m_words[word_index] = (quotient_high << 16) | quotient_low;
|
|
||||||
}
|
|
||||||
remainder.set_to(remainder_word);
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE void UnsignedBigInteger::shift_left_by_n_words(
|
|
||||||
const UnsignedBigInteger& number,
|
|
||||||
const size_t number_of_words,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
// shifting left by N words means just inserting N zeroes to the beginning of the words vector
|
|
||||||
output.set_to_0();
|
|
||||||
output.m_words.resize_and_keep_capacity(number_of_words + number.length());
|
|
||||||
|
|
||||||
__builtin_memset(output.m_words.data(), 0, number_of_words * sizeof(unsigned));
|
|
||||||
__builtin_memcpy(&output.m_words.data()[number_of_words], number.m_words.data(), number.m_words.size() * sizeof(unsigned));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the word at a requested index in the result of a shift operation
|
|
||||||
*/
|
|
||||||
ALWAYS_INLINE u32 UnsignedBigInteger::shift_left_get_one_word(
|
|
||||||
const UnsignedBigInteger& number,
|
|
||||||
const size_t num_bits,
|
|
||||||
const size_t result_word_index)
|
|
||||||
{
|
|
||||||
// "<= length()" (rather than length() - 1) is intentional,
|
|
||||||
// The result inedx of length() is used when calculating the carry word
|
|
||||||
VERIFY(result_word_index <= number.length());
|
|
||||||
VERIFY(num_bits <= UnsignedBigInteger::BITS_IN_WORD);
|
|
||||||
u32 result = 0;
|
|
||||||
|
|
||||||
// we need to check for "num_bits != 0" since shifting right by 32 is apparently undefined behaviour!
|
|
||||||
if (result_word_index > 0 && num_bits != 0) {
|
|
||||||
result += number.m_words[result_word_index - 1] >> (UnsignedBigInteger::BITS_IN_WORD - num_bits);
|
|
||||||
}
|
|
||||||
if (result_word_index < number.length() && num_bits < 32) {
|
|
||||||
result += number.m_words[result_word_index] << num_bits;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AK::Formatter<Crypto::UnsignedBigInteger>::format(FormatBuilder& fmtbuilder, const Crypto::UnsignedBigInteger& value)
|
void AK::Formatter<Crypto::UnsignedBigInteger>::format(FormatBuilder& fmtbuilder, const Crypto::UnsignedBigInteger& value)
|
||||||
|
@ -75,24 +75,12 @@ public:
|
|||||||
|
|
||||||
void set_bit_inplace(size_t bit_index);
|
void set_bit_inplace(size_t bit_index);
|
||||||
|
|
||||||
static void add_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
|
|
||||||
static void subtract_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
|
|
||||||
static void bitwise_or_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
|
|
||||||
static void bitwise_and_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
|
|
||||||
static void bitwise_xor_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
|
|
||||||
static void bitwise_not_without_allocation(const UnsignedBigInteger& left, UnsignedBigInteger& output);
|
|
||||||
static void shift_left_without_allocation(const UnsignedBigInteger& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
|
|
||||||
static void multiply_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
|
|
||||||
static void divide_without_allocation(const UnsignedBigInteger& numerator, const UnsignedBigInteger& denominator, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_minus, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
|
|
||||||
static void divide_u16_without_allocation(const UnsignedBigInteger& numerator, u32 denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
|
|
||||||
|
|
||||||
bool operator==(const UnsignedBigInteger& other) const;
|
bool operator==(const UnsignedBigInteger& other) const;
|
||||||
bool operator!=(const UnsignedBigInteger& other) const;
|
bool operator!=(const UnsignedBigInteger& other) const;
|
||||||
bool operator<(const UnsignedBigInteger& other) const;
|
bool operator<(const UnsignedBigInteger& other) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ALWAYS_INLINE static void shift_left_by_n_words(const UnsignedBigInteger& number, size_t number_of_words, UnsignedBigInteger& output);
|
friend class UnsignedBigIntegerAlgorithms;
|
||||||
ALWAYS_INLINE static u32 shift_left_get_one_word(const UnsignedBigInteger& number, size_t num_bits, size_t result_word_index);
|
|
||||||
|
|
||||||
static constexpr size_t BITS_IN_WORD = 32;
|
static constexpr size_t BITS_IN_WORD = 32;
|
||||||
// Little endian
|
// Little endian
|
||||||
|
@ -5,6 +5,13 @@ set(SOURCES
|
|||||||
ASN1/DER.cpp
|
ASN1/DER.cpp
|
||||||
ASN1/PEM.cpp
|
ASN1/PEM.cpp
|
||||||
Authentication/GHash.cpp
|
Authentication/GHash.cpp
|
||||||
|
BigInt/Algorithms/BitwiseOperations.cpp
|
||||||
|
BigInt/Algorithms/Division.cpp
|
||||||
|
BigInt/Algorithms/GCD.cpp
|
||||||
|
BigInt/Algorithms/ModularInverse.cpp
|
||||||
|
BigInt/Algorithms/ModularPower.cpp
|
||||||
|
BigInt/Algorithms/Multiplication.cpp
|
||||||
|
BigInt/Algorithms/SimpleOperations.cpp
|
||||||
BigInt/SignedBigInteger.cpp
|
BigInt/SignedBigInteger.cpp
|
||||||
BigInt/UnsignedBigInteger.cpp
|
BigInt/UnsignedBigInteger.cpp
|
||||||
Checksum/Adler32.cpp
|
Checksum/Adler32.cpp
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
|
#include <LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h>
|
||||||
#include <LibCrypto/NumberTheory/ModularFunctions.h>
|
#include <LibCrypto/NumberTheory/ModularFunctions.h>
|
||||||
|
|
||||||
namespace Crypto {
|
namespace Crypto {
|
||||||
@ -15,7 +16,6 @@ UnsignedBigInteger ModularInverse(const UnsignedBigInteger& a_, const UnsignedBi
|
|||||||
if (b == 1)
|
if (b == 1)
|
||||||
return { 1 };
|
return { 1 };
|
||||||
|
|
||||||
UnsignedBigInteger one { 1 };
|
|
||||||
UnsignedBigInteger temp_1;
|
UnsignedBigInteger temp_1;
|
||||||
UnsignedBigInteger temp_2;
|
UnsignedBigInteger temp_2;
|
||||||
UnsignedBigInteger temp_3;
|
UnsignedBigInteger temp_3;
|
||||||
@ -23,78 +23,14 @@ UnsignedBigInteger ModularInverse(const UnsignedBigInteger& a_, const UnsignedBi
|
|||||||
UnsignedBigInteger temp_plus;
|
UnsignedBigInteger temp_plus;
|
||||||
UnsignedBigInteger temp_minus;
|
UnsignedBigInteger temp_minus;
|
||||||
UnsignedBigInteger temp_quotient;
|
UnsignedBigInteger temp_quotient;
|
||||||
UnsignedBigInteger temp_remainder;
|
UnsignedBigInteger temp_d;
|
||||||
UnsignedBigInteger d;
|
UnsignedBigInteger temp_u;
|
||||||
|
UnsignedBigInteger temp_v;
|
||||||
|
UnsignedBigInteger temp_x;
|
||||||
|
UnsignedBigInteger result;
|
||||||
|
|
||||||
auto a = a_;
|
UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation(a_, b, temp_1, temp_2, temp_3, temp_4, temp_plus, temp_minus, temp_quotient, temp_d, temp_u, temp_v, temp_x, result);
|
||||||
auto u = a;
|
return result;
|
||||||
if (a.words()[0] % 2 == 0) {
|
|
||||||
// u += b
|
|
||||||
UnsignedBigInteger::add_without_allocation(u, b, temp_plus);
|
|
||||||
u.set_to(temp_plus);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto v = b;
|
|
||||||
UnsignedBigInteger x { 0 };
|
|
||||||
|
|
||||||
// d = b - 1
|
|
||||||
UnsignedBigInteger::subtract_without_allocation(b, one, d);
|
|
||||||
|
|
||||||
while (!(v == 1)) {
|
|
||||||
while (v < u) {
|
|
||||||
// u -= v
|
|
||||||
UnsignedBigInteger::subtract_without_allocation(u, v, temp_minus);
|
|
||||||
u.set_to(temp_minus);
|
|
||||||
|
|
||||||
// d += x
|
|
||||||
UnsignedBigInteger::add_without_allocation(d, x, temp_plus);
|
|
||||||
d.set_to(temp_plus);
|
|
||||||
|
|
||||||
while (u.words()[0] % 2 == 0) {
|
|
||||||
if (d.words()[0] % 2 == 1) {
|
|
||||||
// d += b
|
|
||||||
UnsignedBigInteger::add_without_allocation(d, b, temp_plus);
|
|
||||||
d.set_to(temp_plus);
|
|
||||||
}
|
|
||||||
|
|
||||||
// u /= 2
|
|
||||||
UnsignedBigInteger::divide_u16_without_allocation(u, 2, temp_quotient, temp_remainder);
|
|
||||||
u.set_to(temp_quotient);
|
|
||||||
|
|
||||||
// d /= 2
|
|
||||||
UnsignedBigInteger::divide_u16_without_allocation(d, 2, temp_quotient, temp_remainder);
|
|
||||||
d.set_to(temp_quotient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// v -= u
|
|
||||||
UnsignedBigInteger::subtract_without_allocation(v, u, temp_minus);
|
|
||||||
v.set_to(temp_minus);
|
|
||||||
|
|
||||||
// x += d
|
|
||||||
UnsignedBigInteger::add_without_allocation(x, d, temp_plus);
|
|
||||||
x.set_to(temp_plus);
|
|
||||||
|
|
||||||
while (v.words()[0] % 2 == 0) {
|
|
||||||
if (x.words()[0] % 2 == 1) {
|
|
||||||
// x += b
|
|
||||||
UnsignedBigInteger::add_without_allocation(x, b, temp_plus);
|
|
||||||
x.set_to(temp_plus);
|
|
||||||
}
|
|
||||||
|
|
||||||
// v /= 2
|
|
||||||
UnsignedBigInteger::divide_u16_without_allocation(v, 2, temp_quotient, temp_remainder);
|
|
||||||
v.set_to(temp_quotient);
|
|
||||||
|
|
||||||
// x /= 2
|
|
||||||
UnsignedBigInteger::divide_u16_without_allocation(x, 2, temp_quotient, temp_remainder);
|
|
||||||
x.set_to(temp_quotient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// x % b
|
|
||||||
UnsignedBigInteger::divide_without_allocation(x, b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
|
||||||
return temp_remainder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigInteger& e, const UnsignedBigInteger& m)
|
UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigInteger& e, const UnsignedBigInteger& m)
|
||||||
@ -104,8 +40,8 @@ UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigIn
|
|||||||
|
|
||||||
UnsignedBigInteger ep { e };
|
UnsignedBigInteger ep { e };
|
||||||
UnsignedBigInteger base { b };
|
UnsignedBigInteger base { b };
|
||||||
UnsignedBigInteger exp { 1 };
|
|
||||||
|
|
||||||
|
UnsignedBigInteger result;
|
||||||
UnsignedBigInteger temp_1;
|
UnsignedBigInteger temp_1;
|
||||||
UnsignedBigInteger temp_2;
|
UnsignedBigInteger temp_2;
|
||||||
UnsignedBigInteger temp_3;
|
UnsignedBigInteger temp_3;
|
||||||
@ -114,72 +50,15 @@ UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigIn
|
|||||||
UnsignedBigInteger temp_quotient;
|
UnsignedBigInteger temp_quotient;
|
||||||
UnsignedBigInteger temp_remainder;
|
UnsignedBigInteger temp_remainder;
|
||||||
|
|
||||||
while (!(ep < 1)) {
|
UnsignedBigIntegerAlgorithms::destructive_modular_power_without_allocation(ep, base, m, temp_1, temp_2, temp_3, temp_4, temp_multiply, temp_quotient, temp_remainder, result);
|
||||||
if (ep.words()[0] % 2 == 1) {
|
|
||||||
// exp = (exp * base) % m;
|
|
||||||
UnsignedBigInteger::multiply_without_allocation(exp, base, temp_1, temp_2, temp_3, temp_4, temp_multiply);
|
|
||||||
UnsignedBigInteger::divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
|
||||||
exp.set_to(temp_remainder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ep = ep / 2;
|
return result;
|
||||||
UnsignedBigInteger::divide_u16_without_allocation(ep, 2, temp_quotient, temp_remainder);
|
|
||||||
ep.set_to(temp_quotient);
|
|
||||||
|
|
||||||
// base = (base * base) % m;
|
|
||||||
UnsignedBigInteger::multiply_without_allocation(base, base, temp_1, temp_2, temp_3, temp_4, temp_multiply);
|
|
||||||
UnsignedBigInteger::divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
|
||||||
base.set_to(temp_remainder);
|
|
||||||
|
|
||||||
// Note that not clamping here would cause future calculations (multiply, specifically) to allocate even more unused space
|
|
||||||
// which would then persist through the temp bigints, and significantly slow down later loops.
|
|
||||||
// To avoid that, we can clamp to a specific max size, or just clamp to the min needed amount of space.
|
|
||||||
ep.clamp_to_trimmed_length();
|
|
||||||
exp.clamp_to_trimmed_length();
|
|
||||||
base.clamp_to_trimmed_length();
|
|
||||||
}
|
|
||||||
return exp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GCD_without_allocation(
|
|
||||||
const UnsignedBigInteger& a,
|
|
||||||
const UnsignedBigInteger& b,
|
|
||||||
UnsignedBigInteger& temp_a,
|
|
||||||
UnsignedBigInteger& temp_b,
|
|
||||||
UnsignedBigInteger& temp_1,
|
|
||||||
UnsignedBigInteger& temp_2,
|
|
||||||
UnsignedBigInteger& temp_3,
|
|
||||||
UnsignedBigInteger& temp_4,
|
|
||||||
UnsignedBigInteger& temp_quotient,
|
|
||||||
UnsignedBigInteger& temp_remainder,
|
|
||||||
UnsignedBigInteger& output)
|
|
||||||
{
|
|
||||||
temp_a.set_to(a);
|
|
||||||
temp_b.set_to(b);
|
|
||||||
for (;;) {
|
|
||||||
if (temp_a == 0) {
|
|
||||||
output.set_to(temp_b);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// temp_b %= temp_a
|
|
||||||
UnsignedBigInteger::divide_without_allocation(temp_b, temp_a, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
|
||||||
temp_b.set_to(temp_remainder);
|
|
||||||
if (temp_b == 0) {
|
|
||||||
output.set_to(temp_a);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// temp_a %= temp_b
|
|
||||||
UnsignedBigInteger::divide_without_allocation(temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
|
||||||
temp_a.set_to(temp_remainder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UnsignedBigInteger GCD(const UnsignedBigInteger& a, const UnsignedBigInteger& b)
|
UnsignedBigInteger GCD(const UnsignedBigInteger& a, const UnsignedBigInteger& b)
|
||||||
{
|
{
|
||||||
UnsignedBigInteger temp_a;
|
UnsignedBigInteger temp_a { a };
|
||||||
UnsignedBigInteger temp_b;
|
UnsignedBigInteger temp_b { b };
|
||||||
UnsignedBigInteger temp_1;
|
UnsignedBigInteger temp_1;
|
||||||
UnsignedBigInteger temp_2;
|
UnsignedBigInteger temp_2;
|
||||||
UnsignedBigInteger temp_3;
|
UnsignedBigInteger temp_3;
|
||||||
@ -188,15 +67,15 @@ UnsignedBigInteger GCD(const UnsignedBigInteger& a, const UnsignedBigInteger& b)
|
|||||||
UnsignedBigInteger temp_remainder;
|
UnsignedBigInteger temp_remainder;
|
||||||
UnsignedBigInteger output;
|
UnsignedBigInteger output;
|
||||||
|
|
||||||
GCD_without_allocation(a, b, temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, output);
|
UnsignedBigIntegerAlgorithms::destructive_GCD_without_allocation(temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, output);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
UnsignedBigInteger LCM(const UnsignedBigInteger& a, const UnsignedBigInteger& b)
|
UnsignedBigInteger LCM(const UnsignedBigInteger& a, const UnsignedBigInteger& b)
|
||||||
{
|
{
|
||||||
UnsignedBigInteger temp_a;
|
UnsignedBigInteger temp_a { a };
|
||||||
UnsignedBigInteger temp_b;
|
UnsignedBigInteger temp_b { b };
|
||||||
UnsignedBigInteger temp_1;
|
UnsignedBigInteger temp_1;
|
||||||
UnsignedBigInteger temp_2;
|
UnsignedBigInteger temp_2;
|
||||||
UnsignedBigInteger temp_3;
|
UnsignedBigInteger temp_3;
|
||||||
@ -206,15 +85,15 @@ UnsignedBigInteger LCM(const UnsignedBigInteger& a, const UnsignedBigInteger& b)
|
|||||||
UnsignedBigInteger gcd_output;
|
UnsignedBigInteger gcd_output;
|
||||||
UnsignedBigInteger output { 0 };
|
UnsignedBigInteger output { 0 };
|
||||||
|
|
||||||
GCD_without_allocation(a, b, temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, gcd_output);
|
UnsignedBigIntegerAlgorithms::destructive_GCD_without_allocation(temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, gcd_output);
|
||||||
if (gcd_output == 0) {
|
if (gcd_output == 0) {
|
||||||
dbgln_if(NT_DEBUG, "GCD is zero");
|
dbgln_if(NT_DEBUG, "GCD is zero");
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// output = (a / gcd_output) * b
|
// output = (a / gcd_output) * b
|
||||||
UnsignedBigInteger::divide_without_allocation(a, gcd_output, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
UnsignedBigIntegerAlgorithms::divide_without_allocation(a, gcd_output, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
|
||||||
UnsignedBigInteger::multiply_without_allocation(temp_quotient, b, temp_1, temp_2, temp_3, temp_4, output);
|
UnsignedBigIntegerAlgorithms::multiply_without_allocation(temp_quotient, b, temp_1, temp_2, temp_3, temp_4, output);
|
||||||
|
|
||||||
dbgln_if(NT_DEBUG, "quot: {} rem: {} out: {}", temp_quotient, temp_remainder, output);
|
dbgln_if(NT_DEBUG, "quot: {} rem: {} out: {}", temp_quotient, temp_remainder, output);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user