mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 06:47:34 +00:00
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.
This commit is contained in:
parent
b4591f0037
commit
d8208fd37c
5 changed files with 661 additions and 0 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue