Browse Source

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.
AnotherTest 5 năm trước cách đây
mục cha
commit
d8208fd37c

+ 217 - 0
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;
+}
+
+}

+ 159 - 0
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 });
+}

+ 1 - 0
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

+ 24 - 0
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,

+ 260 - 0
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;
+    }
+}