summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2020-06-04 22:46:18 +0430
committerAndreas Kling <kling@serenityos.org>2020-06-05 13:29:44 +0200
commitd8208fd37c8fe34f1e7eb73d27f732cb9be6ea82 (patch)
treeface003447493ebcf9478772c765a88b5b7ec278
parentb4591f00374c4850075d5454218f4451935eb182 (diff)
downloadserenity-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.cpp217
-rw-r--r--Libraries/LibCrypto/BigInt/SignedBigInteger.h159
-rw-r--r--Libraries/LibCrypto/CMakeLists.txt1
-rw-r--r--Libraries/LibCrypto/NumberTheory/ModularFunctions.h24
-rw-r--r--Userland/test-crypto.cpp260
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;
+ }
+}