diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2020-06-04 22:46:18 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-06-05 13:29:44 +0200 |
commit | d8208fd37c8fe34f1e7eb73d27f732cb9be6ea82 (patch) | |
tree | face003447493ebcf9478772c765a88b5b7ec278 | |
parent | b4591f00374c4850075d5454218f4451935eb182 (diff) | |
download | serenity-d8208fd37c8fe34f1e7eb73d27f732cb9be6ea82.zip |
LibCrypto: Add a simple SignedBigInteger
This patchset adds a simple SignedBigInteger that is entirely defined in
terms of UnsignedBigInteger.
It also adds a NumberTheory::Power function, which is terribly
inefficient, but since the use of exponentiation is very much
discouraged for large inputs, no particular attempts were made
to make it more performant.
-rw-r--r-- | Libraries/LibCrypto/BigInt/SignedBigInteger.cpp | 217 | ||||
-rw-r--r-- | Libraries/LibCrypto/BigInt/SignedBigInteger.h | 159 | ||||
-rw-r--r-- | Libraries/LibCrypto/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Libraries/LibCrypto/NumberTheory/ModularFunctions.h | 24 | ||||
-rw-r--r-- | Userland/test-crypto.cpp | 260 |
5 files changed, 661 insertions, 0 deletions
diff --git a/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp b/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp new file mode 100644 index 0000000000..da17c8935d --- /dev/null +++ b/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SignedBigInteger.h" +#include <AK/StringBuilder.h> + +namespace Crypto { + +SignedBigInteger SignedBigInteger::import_data(const u8* ptr, size_t length) +{ + bool sign = *ptr; + auto unsigned_data = UnsignedBigInteger::import_data(ptr + 1, length - 1); + return { move(unsigned_data), sign }; +} + +size_t SignedBigInteger::export_data(AK::ByteBuffer& data) const +{ + data[0] = m_sign; + auto bytes_view = data.slice_view(1, data.size() - 1); + return m_unsigned_data.export_data(bytes_view) + 1; +} + +SignedBigInteger SignedBigInteger::from_base10(StringView str) +{ + bool sign = false; + if (str.length() > 1) { + auto maybe_sign = str[0]; + if (maybe_sign == '-') { + str = str.substring_view(1, str.length() - 1); + sign = true; + } + if (maybe_sign == '+') + str = str.substring_view(1, str.length() - 1); + } + auto unsigned_data = UnsignedBigInteger::from_base10(str); + return { move(unsigned_data), sign }; +} + +String SignedBigInteger::to_base10() const +{ + StringBuilder builder; + + if (m_sign) + builder.append('-'); + + builder.append(m_unsigned_data.to_base10()); + + return builder.to_string(); +} + +FLATTEN SignedBigInteger SignedBigInteger::plus(const SignedBigInteger& 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); +} + +FLATTEN SignedBigInteger SignedBigInteger::minus(const SignedBigInteger& 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 { other.m_unsigned_data.minus(m_unsigned_data), true }; + } + + // The result will be either zero, or positive. + return SignedBigInteger { 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 { m_unsigned_data.minus(other.m_unsigned_data) }; + } + // The result will be either zero, or negative. + // y - x = - (x - y) + return { other.m_unsigned_data.minus(m_unsigned_data), true }; +} + +FLATTEN SignedBigInteger SignedBigInteger::plus(const UnsignedBigInteger& other) const +{ + if (m_sign) { + if (other < m_unsigned_data) + return { m_unsigned_data.minus(other), true }; + + return { other.minus(m_unsigned_data), false }; + } + + return { m_unsigned_data.plus(other), false }; +} + +FLATTEN SignedBigInteger SignedBigInteger::minus(const UnsignedBigInteger& other) const +{ + if (m_sign) + return { m_unsigned_data.plus(m_unsigned_data), true }; + + if (other < m_unsigned_data) + return { m_unsigned_data.minus(other), false }; + + return { other.minus(m_unsigned_data), true }; +} + +bool SignedBigInteger::operator==(const UnsignedBigInteger& other) const +{ + if (m_sign) + return false; + return m_unsigned_data == other; +} + +bool SignedBigInteger::operator!=(const UnsignedBigInteger& other) const +{ + if (m_sign) + return true; + return m_unsigned_data != other; +} + +bool SignedBigInteger::operator<(const UnsignedBigInteger& other) const +{ + if (m_sign) + return true; + return m_unsigned_data < other; +} + +FLATTEN SignedBigInteger SignedBigInteger::shift_left(size_t num_bits) const +{ + return SignedBigInteger { m_unsigned_data.shift_left(num_bits), m_sign }; +} + +FLATTEN SignedBigInteger SignedBigInteger::multiplied_by(const SignedBigInteger& other) const +{ + bool result_sign = m_sign ^ other.m_sign; + return { m_unsigned_data.multiplied_by(other.m_unsigned_data), result_sign }; +} + +FLATTEN SignedDivisionResult SignedBigInteger::divided_by(const SignedBigInteger& 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 } + }; +} + +void SignedBigInteger::set_bit_inplace(size_t bit_index) +{ + m_unsigned_data.set_bit_inplace(bit_index); +} + +bool SignedBigInteger::operator==(const SignedBigInteger& other) const +{ + if (is_invalid() != other.is_invalid()) + return false; + + 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; +} + +bool SignedBigInteger::operator!=(const SignedBigInteger& other) const +{ + return !(*this == other); +} + +bool SignedBigInteger::operator<(const SignedBigInteger& 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; +} + +} diff --git a/Libraries/LibCrypto/BigInt/SignedBigInteger.h b/Libraries/LibCrypto/BigInt/SignedBigInteger.h new file mode 100644 index 0000000000..d784c007fc --- /dev/null +++ b/Libraries/LibCrypto/BigInt/SignedBigInteger.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <LibCrypto/BigInt/UnsignedBigInteger.h> + +namespace Crypto { + +struct SignedDivisionResult; + +class SignedBigInteger { +public: + SignedBigInteger(i32 x) + : m_sign(x < 0) + , m_unsigned_data(abs(x)) + { + } + + SignedBigInteger(UnsignedBigInteger&& unsigned_data, bool sign) + : m_sign(sign) + , m_unsigned_data(move(unsigned_data)) + { + } + + explicit SignedBigInteger(UnsignedBigInteger unsigned_data) + : m_sign(false) + , m_unsigned_data(move(unsigned_data)) + { + } + + SignedBigInteger() + : m_sign(false) + , m_unsigned_data() + { + } + + static SignedBigInteger create_invalid() + { + return { UnsignedBigInteger::create_invalid(), false }; + } + + static SignedBigInteger import_data(const AK::StringView& data) { return import_data((const u8*)data.characters_without_null_termination(), data.length()); } + static SignedBigInteger import_data(const u8* ptr, size_t length); + + size_t export_data(AK::ByteBuffer& data) const; + size_t export_data(const u8* ptr, size_t length) const + { + auto buffer = ByteBuffer::wrap(ptr, length); + return export_data(buffer); + } + + static SignedBigInteger from_base10(StringView str); + String to_base10() const; + + const UnsignedBigInteger& unsigned_value() const { return m_unsigned_data; } + const Vector<u32, STARTING_WORD_SIZE> words() const { return m_unsigned_data.words(); } + bool is_negative() const { return m_sign; } + + void negate() { m_sign = !m_sign; } + + void set_to_0() { m_unsigned_data.set_to_0(); } + void set_to(i32 other) + { + m_unsigned_data.set_to((u32)other); + m_sign = other < 0; + } + void set_to(const SignedBigInteger& other) + { + m_unsigned_data.set_to(other.m_unsigned_data); + m_sign = other.m_sign; + } + + void invalidate() + { + m_unsigned_data.invalidate(); + } + + bool is_invalid() const { return m_unsigned_data.is_invalid(); } + + // These get + 1 byte for the sign. + size_t length() const { return m_unsigned_data.length() + 1; } + size_t trimmed_length() const { return m_unsigned_data.trimmed_length() + 1; }; + + SignedBigInteger plus(const SignedBigInteger& other) const; + SignedBigInteger minus(const SignedBigInteger& other) const; + SignedBigInteger shift_left(size_t num_bits) const; + SignedBigInteger multiplied_by(const SignedBigInteger& other) const; + SignedDivisionResult divided_by(const SignedBigInteger& divisor) const; + + SignedBigInteger plus(const UnsignedBigInteger& other) const; + SignedBigInteger minus(const UnsignedBigInteger& other) const; + SignedBigInteger multiplied_by(const UnsignedBigInteger& other) const; + SignedDivisionResult divided_by(const UnsignedBigInteger& divisor) const; + + void set_bit_inplace(size_t bit_index); + + bool operator==(const SignedBigInteger& other) const; + bool operator!=(const SignedBigInteger& other) const; + bool operator<(const SignedBigInteger& other) const; + + bool operator==(const UnsignedBigInteger& other) const; + bool operator!=(const UnsignedBigInteger& other) const; + bool operator<(const UnsignedBigInteger& other) const; + +private: + bool m_sign { false }; + UnsignedBigInteger m_unsigned_data; +}; + +struct SignedDivisionResult { + Crypto::SignedBigInteger quotient; + Crypto::SignedBigInteger remainder; +}; + +} + +inline const LogStream& +operator<<(const LogStream& stream, const Crypto::SignedBigInteger value) +{ + if (value.is_invalid()) { + stream << "Invalid BigInt"; + return stream; + } + if (value.is_negative()) + stream << "-"; + + stream << value.unsigned_value(); + return stream; +} + +inline Crypto::SignedBigInteger +operator""_sbigint(const char* string, size_t length) +{ + return Crypto::SignedBigInteger::from_base10({ string, length }); +} diff --git a/Libraries/LibCrypto/CMakeLists.txt b/Libraries/LibCrypto/CMakeLists.txt index b8306128bb..c644b1eec4 100644 --- a/Libraries/LibCrypto/CMakeLists.txt +++ b/Libraries/LibCrypto/CMakeLists.txt @@ -1,5 +1,6 @@ set(SOURCES BigInt/UnsignedBigInteger.cpp + BigInt/SignedBigInteger.cpp Cipher/AES.cpp Hash/MD5.cpp Hash/SHA1.cpp diff --git a/Libraries/LibCrypto/NumberTheory/ModularFunctions.h b/Libraries/LibCrypto/NumberTheory/ModularFunctions.h index d7a875945b..d354a8dac2 100644 --- a/Libraries/LibCrypto/NumberTheory/ModularFunctions.h +++ b/Libraries/LibCrypto/NumberTheory/ModularFunctions.h @@ -161,6 +161,30 @@ static auto ModularPower(const UnsignedBigInteger& b, const UnsignedBigInteger& return exp; } +// Note: This function _will_ generate extremely huge numbers, and in doing so, +// it will allocate and free a lot of memory! +// Please use |ModularPower| if your use-case is modexp. +template<typename IntegerType> +static auto Power(const IntegerType& b, const IntegerType& e) -> IntegerType +{ + IntegerType ep { e }; + IntegerType base { b }; + IntegerType 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.divided_by(2).quotient); + + // base = base * base + base.set_to(base.multiplied_by(base)); + } + + return exp; +} + static void GCD_without_allocation( const UnsignedBigInteger& a, const UnsignedBigInteger& b, diff --git a/Userland/test-crypto.cpp b/Userland/test-crypto.cpp index 88f5ff8b86..ca5f6abbbf 100644 --- a/Userland/test-crypto.cpp +++ b/Userland/test-crypto.cpp @@ -29,6 +29,7 @@ #include <LibCore/EventLoop.h> #include <LibCore/File.h> #include <LibCrypto/Authentication/HMAC.h> +#include <LibCrypto/BigInt/SignedBigInteger.h> #include <LibCrypto/BigInt/UnsignedBigInteger.h> #include <LibCrypto/Cipher/AES.h> #include <LibCrypto/Hash/MD5.h> @@ -486,6 +487,14 @@ void bigint_division(); void bigint_base10(); void bigint_import_export(); +void bigint_test_signed_fibo500(); +void bigint_signed_addition_edgecases(); +void bigint_signed_subtraction(); +void bigint_signed_multiplication(); +void bigint_signed_division(); +void bigint_signed_base10(); +void bigint_signed_import_export(); + int aes_cbc_tests() { aes_cbc_test_name(); @@ -1285,6 +1294,15 @@ int bigint_tests() bigint_division(); bigint_base10(); bigint_import_export(); + + bigint_test_signed_fibo500(); + bigint_signed_addition_edgecases(); + bigint_signed_subtraction(); + bigint_signed_multiplication(); + bigint_signed_division(); + bigint_signed_base10(); + bigint_signed_import_export(); + return 0; } @@ -1299,6 +1317,18 @@ Crypto::UnsignedBigInteger bigint_fibonacci(size_t n) } return num1; } + +Crypto::SignedBigInteger bigint_signed_fibonacci(size_t n) +{ + Crypto::SignedBigInteger num1(0); + Crypto::SignedBigInteger num2(1); + for (size_t i = 0; i < n; ++i) { + Crypto::SignedBigInteger t = num1.plus(num2); + num2 = num1; + num1 = t; + } + return num1; +} void bigint_test_fibo500() { { @@ -1555,3 +1585,233 @@ void bigint_import_export() } } } + +void bigint_test_signed_fibo500() +{ + { + I_TEST((Signed BigInteger | Fibonacci500)); + bool pass = (bigint_signed_fibonacci(500).unsigned_value().words() == AK::Vector<u32> { 315178285, 505575602, 1883328078, 125027121, 3649625763, 347570207, 74535262, 3832543808, 2472133297, 1600064941, 65273441 }); + + if (pass) { + PASS; + } else { + FAIL(Incorrect Result); + } + } +} + +void bigint_signed_addition_edgecases() +{ + { + I_TEST((Signed BigInteger | Borrow with zero)); + Crypto::SignedBigInteger num1 { Crypto::UnsignedBigInteger { { UINT32_MAX - 3, UINT32_MAX } }, false }; + Crypto::SignedBigInteger num2 { Crypto::UnsignedBigInteger { UINT32_MAX - 2 }, false }; + if (num1.plus(num2).unsigned_value().words() == Vector<u32> { 4294967289, 0, 1 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | Addition to other sign)); + Crypto::SignedBigInteger num1 = INT32_MAX; + Crypto::SignedBigInteger num2 = num1; + num2.negate(); + if (num1.plus(num2) == Crypto::SignedBigInteger { 0 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } +} + +void bigint_signed_subtraction() +{ + { + I_TEST((Signed BigInteger | Simple Subtraction 1)); + Crypto::SignedBigInteger num1(80); + Crypto::SignedBigInteger num2(70); + + if (num1.minus(num2) == Crypto::SignedBigInteger(10)) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | Simple Subtraction 2)); + Crypto::SignedBigInteger num1(50); + Crypto::SignedBigInteger num2(70); + + if (num1.minus(num2) == Crypto::SignedBigInteger { -20 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | Subtraction with borrow)); + Crypto::SignedBigInteger num1(Crypto::UnsignedBigInteger { UINT32_MAX }); + Crypto::SignedBigInteger num2(1); + Crypto::SignedBigInteger num3 = num1.plus(num2); + Crypto::SignedBigInteger result = num2.minus(num3); + num1.negate(); + if (result == num1) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | Subtraction with large numbers)); + Crypto::SignedBigInteger num1 = bigint_signed_fibonacci(343); + Crypto::SignedBigInteger num2 = bigint_signed_fibonacci(218); + Crypto::SignedBigInteger result = num2.minus(num1); + auto expected = Crypto::UnsignedBigInteger { Vector<u32> { 811430588, 2958904896, 1130908877, 2830569969, 3243275482, 3047460725, 774025231, 7990 } }; + if ((result.plus(num1) == num2) + && (result.unsigned_value() == expected)) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | Subtraction with large numbers 2)); + Crypto::SignedBigInteger num1(Crypto::UnsignedBigInteger { Vector<u32> { 1483061863, 446680044, 1123294122, 191895498, 3347106536, 16, 0, 0, 0 } }); + Crypto::SignedBigInteger num2(Crypto::UnsignedBigInteger { Vector<u32> { 4196414175, 1117247942, 1123294122, 191895498, 3347106536, 16 } }); + Crypto::SignedBigInteger result = num1.minus(num2); + // this test only verifies that we don't crash on an assertion + PASS; + } +} + +void bigint_signed_multiplication() +{ + { + I_TEST((Signed BigInteger | Simple Multiplication)); + Crypto::SignedBigInteger num1(8); + Crypto::SignedBigInteger num2(-251); + Crypto::SignedBigInteger result = num1.multiplied_by(num2); + if (result == Crypto::SignedBigInteger { -2008 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | Multiplications with big numbers 1)); + Crypto::SignedBigInteger num1 = bigint_signed_fibonacci(200); + Crypto::SignedBigInteger num2(-12345678); + Crypto::SignedBigInteger result = num1.multiplied_by(num2); + if (result.unsigned_value().words() == Vector<u32> { 669961318, 143970113, 4028714974, 3164551305, 1589380278, 2 } && result.is_negative()) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | Multiplications with big numbers 2)); + Crypto::SignedBigInteger num1 = bigint_signed_fibonacci(200); + Crypto::SignedBigInteger num2 = bigint_signed_fibonacci(341); + num1.negate(); + Crypto::SignedBigInteger result = num1.multiplied_by(num2); + if (result.unsigned_value().words() == Vector<u32> { 3017415433, 2741793511, 1957755698, 3731653885, 3154681877, 785762127, 3200178098, 4260616581, 529754471, 3632684436, 1073347813, 2516430 } && result.is_negative()) { + PASS; + } else { + FAIL(Incorrect Result); + } + } +} +void bigint_signed_division() +{ + { + I_TEST((Signed BigInteger | Simple Division)); + Crypto::SignedBigInteger num1(27194); + Crypto::SignedBigInteger num2(-251); + auto result = num1.divided_by(num2); + Crypto::SignedDivisionResult expected = { Crypto::SignedBigInteger(-108), Crypto::SignedBigInteger(86) }; + if (result.quotient == expected.quotient && result.remainder == expected.remainder) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | Division with big numbers)); + Crypto::SignedBigInteger num1 = bigint_signed_fibonacci(386); + Crypto::SignedBigInteger num2 = bigint_signed_fibonacci(238); + num1.negate(); + auto result = num1.divided_by(num2); + Crypto::SignedDivisionResult expected = { + Crypto::SignedBigInteger(Crypto::UnsignedBigInteger { Vector<u32> { 2300984486, 2637503534, 2022805584, 107 } }, true), + Crypto::SignedBigInteger(Crypto::UnsignedBigInteger { Vector<u32> { 1483061863, 446680044, 1123294122, 191895498, 3347106536, 16, 0, 0, 0 } }, true) + }; + if (result.quotient == expected.quotient && result.remainder == expected.remainder) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | Combined test)); + auto num1 = bigint_signed_fibonacci(497); + auto num2 = bigint_signed_fibonacci(238); + num1.negate(); + auto div_result = num1.divided_by(num2); + if (div_result.quotient.multiplied_by(num2).plus(div_result.remainder) == num1) { + PASS; + } else { + FAIL(Incorrect Result); + } + } +} + +void bigint_signed_base10() +{ + { + I_TEST((Signed BigInteger | From String)); + auto result = Crypto::SignedBigInteger::from_base10("-57195071295721390579057195715793"); + if (result.unsigned_value().words() == Vector<u32> { 3806301393, 954919431, 3879607298, 721 } && result.is_negative()) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((Signed BigInteger | To String)); + auto result = Crypto::SignedBigInteger { Crypto::UnsignedBigInteger { Vector<u32> { 3806301393, 954919431, 3879607298, 721 } }, true }.to_base10(); + if (result == "-57195071295721390579057195715793") { + PASS; + } else { + FAIL(Incorrect Result); + } + } +} + +void bigint_signed_import_export() +{ + { + I_TEST((Signed BigInteger | BigEndian Decode / Encode roundtrip)); + u8 random_bytes[129]; + u8 target_buffer[129]; + random_bytes[0] = 1; + AK::fill_with_random(random_bytes + 1, 128); + auto encoded = Crypto::SignedBigInteger::import_data(random_bytes, 129); + encoded.export_data(target_buffer, 129); + if (memcmp(target_buffer, random_bytes, 129) != 0) + FAIL(Could not roundtrip); + else + PASS; + } + { + I_TEST((Signed BigInteger | BigEndian Encode / Decode roundtrip)); + u8 target_buffer[128]; + auto encoded = "-12345678901234567890"_sbigint; + auto size = encoded.export_data(target_buffer, 128); + auto decoded = Crypto::SignedBigInteger::import_data(target_buffer, size); + if (encoded != decoded) + FAIL(Could not roundtrip); + else + PASS; + } +} |