Browse Source

LibCrypto: Add bitwise operations (and/or/xor)

AnotherTest 5 years ago
parent
commit
02c53fd1f9

+ 51 - 0
Libraries/LibCrypto/BigInt/SignedBigInteger.cpp

@@ -139,6 +139,57 @@ FLATTEN SignedBigInteger SignedBigInteger::minus(const UnsignedBigInteger& other
     return { other.minus(m_unsigned_data), true };
 }
 
+FLATTEN SignedBigInteger SignedBigInteger::bitwise_or(const UnsignedBigInteger& other) const
+{
+    return { unsigned_value().bitwise_or(other), m_sign };
+}
+
+FLATTEN SignedBigInteger SignedBigInteger::bitwise_and(const UnsignedBigInteger& other) const
+{
+    return { unsigned_value().bitwise_and(other), false };
+}
+
+FLATTEN SignedBigInteger SignedBigInteger::bitwise_xor(const UnsignedBigInteger& other) const
+{
+    return { unsigned_value().bitwise_xor(other), m_sign };
+}
+
+FLATTEN SignedBigInteger SignedBigInteger::bitwise_not() const
+{
+    return { unsigned_value().bitwise_not(), !m_sign };
+}
+
+FLATTEN SignedBigInteger SignedBigInteger::bitwise_or(const SignedBigInteger& other) const
+{
+    auto result = bitwise_or(other.unsigned_value());
+
+    // The sign bit will have to be OR'd manually.
+    if (other.is_negative())
+        result.negate();
+
+    return result;
+}
+
+FLATTEN SignedBigInteger SignedBigInteger::bitwise_and(const SignedBigInteger& other) const
+{
+    auto result = bitwise_and(other.unsigned_value());
+
+    // The sign bit will have to be AND'd manually.
+    result.m_sign = is_negative() || other.is_negative();
+
+    return result;
+}
+
+FLATTEN SignedBigInteger SignedBigInteger::bitwise_xor(const SignedBigInteger& other) const
+{
+    auto result = bitwise_xor(other.unsigned_value());
+
+    // The sign bit will have to be XOR'd manually.
+    result.m_sign = is_negative() ^ other.is_negative();
+
+    return result;
+}
+
 bool SignedBigInteger::operator==(const UnsignedBigInteger& other) const
 {
     if (m_sign)

+ 7 - 0
Libraries/LibCrypto/BigInt/SignedBigInteger.h

@@ -107,12 +107,19 @@ public:
 
     SignedBigInteger plus(const SignedBigInteger& other) const;
     SignedBigInteger minus(const SignedBigInteger& other) const;
+    SignedBigInteger bitwise_or(const SignedBigInteger& other) const;
+    SignedBigInteger bitwise_and(const SignedBigInteger& other) const;
+    SignedBigInteger bitwise_xor(const SignedBigInteger& other) const;
+    SignedBigInteger bitwise_not() 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 bitwise_or(const UnsignedBigInteger& other) const;
+    SignedBigInteger bitwise_and(const UnsignedBigInteger& other) const;
+    SignedBigInteger bitwise_xor(const UnsignedBigInteger& other) const;
     SignedBigInteger multiplied_by(const UnsignedBigInteger& other) const;
     SignedDivisionResult divided_by(const UnsignedBigInteger& divisor) const;
 

+ 183 - 0
Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp

@@ -158,6 +158,42 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::minus(const UnsignedBigInteger& o
     return result;
 }
 
+FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_or(const UnsignedBigInteger& other) const
+{
+    UnsignedBigInteger result;
+
+    bitwise_or_without_allocation(*this, other, result);
+
+    return result;
+}
+
+FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_and(const UnsignedBigInteger& other) const
+{
+    UnsignedBigInteger result;
+
+    bitwise_and_without_allocation(*this, other, result);
+
+    return result;
+}
+
+FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_xor(const UnsignedBigInteger& other) const
+{
+    UnsignedBigInteger result;
+
+    bitwise_xor_without_allocation(*this, other, result);
+
+    return result;
+}
+
+FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_not() const
+{
+    UnsignedBigInteger result;
+
+    bitwise_not_without_allocation(*this, result);
+
+    return result;
+}
+
 FLATTEN UnsignedBigInteger UnsignedBigInteger::shift_left(size_t num_bits) const
 {
     UnsignedBigInteger output;
@@ -340,6 +376,153 @@ void UnsignedBigInteger::subtract_without_allocation(
     ASSERT(borrow == 0);
 }
 
+/**
+ * Complexity: O(N) where N is the number of words in the shorter value
+ * Method:
+ * Apply <op> word-wise until words in the shorter value are used up
+ * then copy the rest of the words verbatim from the longer value.
+ */
+FLATTEN void UnsignedBigInteger::bitwise_or_without_allocation(
+    const UnsignedBigInteger& left,
+    const UnsignedBigInteger& right,
+    UnsignedBigInteger& output)
+{
+    // If either of the BigInts are invalid, the output is just the other one.
+    if (left.is_invalid()) {
+        output.set_to(right);
+        return;
+    }
+    if (right.is_invalid()) {
+        output.set_to(left);
+        return;
+    }
+
+    const UnsignedBigInteger *shorter, *longer;
+    if (left.length() < right.length()) {
+        shorter = &left;
+        longer = &right;
+    } else {
+        shorter = &right;
+        longer = &left;
+    }
+
+    output.m_words.resize_and_keep_capacity(longer->length());
+
+    size_t longer_offset = longer->length() - shorter->length();
+    for (size_t i = 0; i < shorter->length(); ++i)
+        output.m_words[i] = longer->words()[i] | shorter->words()[i];
+
+    __builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset);
+}
+
+/**
+ * Complexity: O(N) where N is the number of words in the shorter value
+ * Method:
+ * Apply 'and' word-wise until words in the shorter value are used up
+ * and zero the rest.
+ */
+FLATTEN void UnsignedBigInteger::bitwise_and_without_allocation(
+    const UnsignedBigInteger& left,
+    const UnsignedBigInteger& right,
+    UnsignedBigInteger& output)
+{
+    // If either of the BigInts are invalid, the output is just the other one.
+    if (left.is_invalid()) {
+        output.set_to(right);
+        return;
+    }
+    if (right.is_invalid()) {
+        output.set_to(left);
+        return;
+    }
+
+    const UnsignedBigInteger *shorter, *longer;
+    if (left.length() < right.length()) {
+        shorter = &left;
+        longer = &right;
+    } else {
+        shorter = &right;
+        longer = &left;
+    }
+
+    output.m_words.resize_and_keep_capacity(longer->length());
+
+    size_t longer_offset = longer->length() - shorter->length();
+    for (size_t i = 0; i < shorter->length(); ++i)
+        output.m_words[i] = longer->words()[i] & shorter->words()[i];
+
+    __builtin_memset(output.m_words.data() + shorter->length(), 0, sizeof(u32) * longer_offset);
+}
+
+/**
+ * Complexity: O(N) where N is the number of words in the shorter value
+ * Method:
+ * Apply 'xor' word-wise until words in the shorter value are used up
+ * and copy the rest.
+ */
+FLATTEN void UnsignedBigInteger::bitwise_xor_without_allocation(
+    const UnsignedBigInteger& left,
+    const UnsignedBigInteger& right,
+    UnsignedBigInteger& output)
+{
+    // If either of the BigInts are invalid, the output is just the other one.
+    if (left.is_invalid()) {
+        output.set_to(right);
+        return;
+    }
+    if (right.is_invalid()) {
+        output.set_to(left);
+        return;
+    }
+
+    const UnsignedBigInteger *shorter, *longer;
+    if (left.length() < right.length()) {
+        shorter = &left;
+        longer = &right;
+    } else {
+        shorter = &right;
+        longer = &left;
+    }
+
+    output.m_words.resize_and_keep_capacity(longer->length());
+
+    size_t longer_offset = longer->length() - shorter->length();
+    for (size_t i = 0; i < shorter->length(); ++i)
+        output.m_words[i] = longer->words()[i] ^ shorter->words()[i];
+
+    __builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset);
+}
+
+/**
+ * Complexity: O(N) where N is the number of words
+ */
+FLATTEN void UnsignedBigInteger::bitwise_not_without_allocation(
+    const UnsignedBigInteger& right,
+    UnsignedBigInteger& output)
+{
+    // If the value is invalid, the output value is invalid as well.
+    if (right.is_invalid()) {
+        output.invalidate();
+        return;
+    }
+    if (right.length() == 0) {
+        output.set_to_0();
+        return;
+    }
+
+    output.m_words.resize_and_keep_capacity(right.length());
+
+    if (right.length() > 1) {
+        for (size_t i = 0; i < right.length() - 1; ++i)
+            output.m_words[i] = ~right.words()[i];
+    }
+
+    auto last_word_index = right.length() - 1;
+    auto last_word = right.words()[last_word_index];
+
+    output.m_words[last_word_index] = ((u32)0xffffffffffffffff >> __builtin_clz(last_word)) & ~last_word;
+}
+
 /**
  * Complexity : O(N + num_bits % 8) where N is the number of words in the number
  * Shift method :

+ 8 - 0
Libraries/LibCrypto/BigInt/UnsignedBigInteger.h

@@ -83,6 +83,10 @@ public:
 
     UnsignedBigInteger plus(const UnsignedBigInteger& other) const;
     UnsignedBigInteger minus(const UnsignedBigInteger& other) const;
+    UnsignedBigInteger bitwise_or(const UnsignedBigInteger& other) const;
+    UnsignedBigInteger bitwise_and(const UnsignedBigInteger& other) const;
+    UnsignedBigInteger bitwise_xor(const UnsignedBigInteger& other) const;
+    UnsignedBigInteger bitwise_not() const;
     UnsignedBigInteger shift_left(size_t num_bits) const;
     UnsignedBigInteger multiplied_by(const UnsignedBigInteger& other) const;
     UnsignedDivisionResult divided_by(const UnsignedBigInteger& divisor) const;
@@ -91,6 +95,10 @@ public:
 
     static void add_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
     static void subtract_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
+    static void bitwise_or_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
+    static void bitwise_and_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
+    static void bitwise_xor_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
+    static void bitwise_not_without_allocation(const UnsignedBigInteger& left, UnsignedBigInteger& output);
     static void shift_left_without_allocation(const UnsignedBigInteger& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
     static void multiply_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
     static void divide_without_allocation(const UnsignedBigInteger& numerator, const UnsignedBigInteger& denominator, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_minus, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);

+ 84 - 0
Userland/test-crypto.cpp

@@ -486,6 +486,7 @@ void bigint_multiplication();
 void bigint_division();
 void bigint_base10();
 void bigint_import_export();
+void bigint_bitwise();
 
 void bigint_test_signed_fibo500();
 void bigint_signed_addition_edgecases();
@@ -494,6 +495,7 @@ void bigint_signed_multiplication();
 void bigint_signed_division();
 void bigint_signed_base10();
 void bigint_signed_import_export();
+void bigint_signed_bitwise();
 
 int aes_cbc_tests()
 {
@@ -1294,6 +1296,7 @@ int bigint_tests()
     bigint_division();
     bigint_base10();
     bigint_import_export();
+    bigint_bitwise();
 
     bigint_test_signed_fibo500();
     bigint_signed_addition_edgecases();
@@ -1302,6 +1305,7 @@ int bigint_tests()
     bigint_signed_division();
     bigint_signed_base10();
     bigint_signed_import_export();
+    bigint_signed_bitwise();
 
     return 0;
 }
@@ -1586,6 +1590,72 @@ void bigint_import_export()
     }
 }
 
+void bigint_bitwise()
+{
+    {
+        I_TEST((BigInteger | Basic bitwise or));
+        auto num1 = "1234567"_bigint;
+        auto num2 = "1234567"_bigint;
+        if (num1.bitwise_or(num2) == num1) {
+            PASS;
+        } else {
+            FAIL(Invalid value);
+        }
+    }
+    {
+        I_TEST((BigInteger | Bitwise or handles different lengths));
+        auto num1 = "1234567"_bigint;
+        auto num2 = "123456789012345678901234567890"_bigint;
+        auto expected = "123456789012345678901234622167"_bigint;
+        auto result = num1.bitwise_or(num2);
+        if (result == expected) {
+            PASS;
+        } else {
+            FAIL(Invalid value);
+        }
+    }
+    {
+        I_TEST((BigInteger | Basic bitwise and));
+        auto num1 = "1234567"_bigint;
+        auto num2 = "1234561"_bigint;
+        if (num1.bitwise_and(num2) == "1234561"_bigint) {
+            PASS;
+        } else {
+            FAIL(Invalid value);
+        }
+    }
+    {
+        I_TEST((BigInteger | Bitwise and handles different lengths));
+        auto num1 = "1234567"_bigint;
+        auto num2 = "123456789012345678901234567890"_bigint;
+        if (num1.bitwise_and(num2) == "1180290"_bigint) {
+            PASS;
+        } else {
+            FAIL(Invalid value);
+        }
+    }
+    {
+        I_TEST((BigInteger | Basic bitwise xor));
+        auto num1 = "1234567"_bigint;
+        auto num2 = "1234561"_bigint;
+        if (num1.bitwise_xor(num2) == 6) {
+            PASS;
+        } else {
+            FAIL(Invalid value);
+        }
+    }
+    {
+        I_TEST((BigInteger | Bitwise xor handles different lengths));
+        auto num1 = "1234567"_bigint;
+        auto num2 = "123456789012345678901234567890"_bigint;
+        if (num1.bitwise_xor(num2) == "123456789012345678901233441877"_bigint) {
+            PASS;
+        } else {
+            FAIL(Invalid value);
+        }
+    }
+}
+
 void bigint_test_signed_fibo500()
 {
     {
@@ -1815,3 +1885,17 @@ void bigint_signed_import_export()
             PASS;
     }
 }
+
+void bigint_signed_bitwise()
+{
+    {
+        I_TEST((Signed BigInteger | Bitwise or handles sign));
+        auto num1 = "-1234567"_sbigint;
+        auto num2 = "1234567"_sbigint;
+        if (num1.bitwise_or(num2) == num1) {
+            PASS;
+        } else {
+            FAIL(Invalid value);
+        }
+    }
+}