diff --git a/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp b/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp index 87ffab03e7b..93bb1951c1f 100644 --- a/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp +++ b/Libraries/LibCrypto/BigInt/Algorithms/BitwiseOperations.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2020, Itamar S. * Copyright (c) 2020-2021, Dex♪ + * Copyright (c) 2025, Manuel Zahariev * * SPDX-License-Identifier: BSD-2-Clause */ @@ -221,13 +222,49 @@ FLATTEN ErrorOr UnsignedBigIntegerAlgorithms::try_shift_left_without_alloc 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) { - output.m_words.resize_and_keep_capacity(number.length() - (num_bits / UnsignedBigInteger::BITS_IN_WORD)); - Ops::shift_right(number.words_span(), num_bits, output.words_span()); + 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( diff --git a/Tests/LibCrypto/TestBigInteger.cpp b/Tests/LibCrypto/TestBigInteger.cpp index c87f90c4d3a..dee397658bb 100644 --- a/Tests/LibCrypto/TestBigInteger.cpp +++ b/Tests/LibCrypto/TestBigInteger.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -570,6 +571,40 @@ TEST_CASE(test_signed_bigint_bitwise_xor) EXPECT_EQ(num2.bitwise_xor(num2), "0"_sbigint); } +TEST_CASE(test_bigint_shift_right) +{ + Crypto::UnsignedBigInteger const num1(Vector { 0x100, 0x20, 0x4, 0x2, 0x1 }); + + size_t const tests1 = 11; + AK::Tuple> results1[] = { + { 8, { 0x20000001, 0x04000000, 0x02000000, 0x01000000 } }, + { 16, { 0x00200000, 0x00040000, 0x00020000, 0x00010000 } }, // shift by exact number of words + { 32, { 0x00000020, 0x00000004, 0x00000002, 0x00000001 } }, // shift by exact number of words + { 36, { 0x40000002, 0x20000000, 0x10000000 } }, + { 64, { 0x00000004, 0x00000002, 0x00000001 } }, // shift by exact number of words + { 72, { 0x02000000, 0x01000000 } }, + { 80, { 0x00020000, 0x00010000 } }, + { 88, { 0x00000200, 0x00000100 } }, + { 128, { 0x00000001 } }, // shifted to most significant digit + { 129, {} }, // all digits have been shifted right + { 160, {} }, + }; + + size_t const tests2 = 2; + Crypto::UnsignedBigInteger const num2(Vector { 0x44444444, 0xffffffff }); + + AK::Tuple> results2[] = { + { 1, { 0xa2222222, 0x7fffffff } }, + { 2, { 0xd1111111, 0x3fffffff } }, + }; + + for (size_t i = 0; i < tests1; ++i) + EXPECT_EQ(num1.shift_right(results1[i].get<0>()).words(), results1[i].get<1>()); + + for (size_t i = 0; i < tests2; ++i) + EXPECT_EQ(num2.shift_right(results2[i].get<0>()).words(), results2[i].get<1>()); +} + TEST_CASE(test_signed_bigint_fibo500) { Vector expected_result {