diff --git a/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp b/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp index 0563446425f..4ba22dd1f7e 100644 --- a/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp +++ b/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp @@ -114,122 +114,4 @@ UnsignedBigInteger LCM(UnsignedBigInteger const& a, UnsignedBigInteger const& b) return output; } -static bool MR_primality_test(UnsignedBigInteger n, Vector const& tests) -{ - // Written using Wikipedia: - // https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Miller%E2%80%93Rabin_test - VERIFY(!(n < 4)); - auto predecessor = n.minus({ 1 }); - auto d = predecessor; - size_t r = 0; - - { - auto div_result = d.divided_by(2); - while (div_result.remainder == 0) { - d = div_result.quotient; - div_result = d.divided_by(2); - ++r; - } - } - if (r == 0) { - // n - 1 is odd, so n was even. But there is only one even prime: - return n == 2; - } - - for (auto& a : tests) { - // Technically: VERIFY(2 <= a && a <= n - 2) - VERIFY(a < n); - auto x = ModularPower(a, d, n); - if (x == 1 || x == predecessor) - continue; - bool skip_this_witness = false; - // r − 1 iterations. - for (size_t i = 0; i < r - 1; ++i) { - x = ModularPower(x, 2, n); - if (x == predecessor) { - skip_this_witness = true; - break; - } - } - if (skip_this_witness) - continue; - return false; // "composite" - } - - return true; // "probably prime" -} - -UnsignedBigInteger random_number(UnsignedBigInteger const& min, UnsignedBigInteger const& max_excluded) -{ - VERIFY(min < max_excluded); - auto range = max_excluded.minus(min); - UnsignedBigInteger base; - auto size = range.trimmed_length() * sizeof(u32) + 2; - // "+2" is intentional (see below). - auto buffer = ByteBuffer::create_uninitialized(size).release_value_but_fixme_should_propagate_errors(); // FIXME: Handle possible OOM situation. - auto* buf = buffer.data(); - - fill_with_secure_random(buffer); - UnsignedBigInteger random { buf, size }; - // At this point, `random` is a large number, in the range [0, 256^size). - // To get down to the actual range, we could just compute random % range. - // This introduces "modulo bias". However, since we added 2 to `size`, - // we know that the generated range is at least 65536 times as large as the - // required range! This means that the modulo bias is only 0.0015%, if all - // inputs are chosen adversarially. Let's hope this is good enough. - auto divmod = random.divided_by(range); - // The proper way to fix this is to restart if `divmod.quotient` is maximal. - return divmod.remainder.plus(min); -} - -bool is_probably_prime(UnsignedBigInteger const& p) -{ - // Is it a small number? - if (p < 49) { - u32 p_value = p.words()[0]; - // Is it a very small prime? - if (p_value == 2 || p_value == 3 || p_value == 5 || p_value == 7) - return true; - // Is it the multiple of a very small prime? - if (p_value % 2 == 0 || p_value % 3 == 0 || p_value % 5 == 0 || p_value % 7 == 0) - return false; - // Then it must be a prime, but not a very small prime, like 37. - return true; - } - - Vector tests; - // Make some good initial guesses that are guaranteed to find all primes < 2^64. - tests.append(UnsignedBigInteger(2)); - tests.append(UnsignedBigInteger(3)); - tests.append(UnsignedBigInteger(5)); - tests.append(UnsignedBigInteger(7)); - tests.append(UnsignedBigInteger(11)); - tests.append(UnsignedBigInteger(13)); - UnsignedBigInteger seventeen { 17 }; - for (size_t i = tests.size(); i < 256; ++i) { - tests.append(random_number(seventeen, p.minus(2))); - } - // Miller-Rabin's "error" is 8^-k. In adversarial cases, it's 4^-k. - // With 200 random numbers, this would mean an error of about 2^-400. - // So we don't need to worry too much about the quality of the random numbers. - - return MR_primality_test(p, tests); -} - -UnsignedBigInteger random_big_prime(size_t bits) -{ - VERIFY(bits >= 33); - UnsignedBigInteger min = "6074001000"_bigint.shift_left(bits - 33); - UnsignedBigInteger max = UnsignedBigInteger { 1 }.shift_left(bits).minus(1); - for (;;) { - auto p = random_number(min, max); - if ((p.words()[0] & 1) == 0) { - // An even number is definitely not a large prime. - continue; - } - if (is_probably_prime(p)) - return p; - } -} - } diff --git a/Libraries/LibCrypto/NumberTheory/ModularFunctions.h b/Libraries/LibCrypto/NumberTheory/ModularFunctions.h index 3204fe0fbf7..320d28a62ca 100644 --- a/Libraries/LibCrypto/NumberTheory/ModularFunctions.h +++ b/Libraries/LibCrypto/NumberTheory/ModularFunctions.h @@ -41,8 +41,4 @@ static IntegerType Power(IntegerType const& b, IntegerType const& e) UnsignedBigInteger GCD(UnsignedBigInteger const& a, UnsignedBigInteger const& b); UnsignedBigInteger LCM(UnsignedBigInteger const& a, UnsignedBigInteger const& b); -UnsignedBigInteger random_number(UnsignedBigInteger const& min, UnsignedBigInteger const& max_excluded); -bool is_probably_prime(UnsignedBigInteger const& p); -UnsignedBigInteger random_big_prime(size_t bits); - } diff --git a/Tests/LibCrypto/TestBigInteger.cpp b/Tests/LibCrypto/TestBigInteger.cpp index e3e3300da39..e7cea95f508 100644 --- a/Tests/LibCrypto/TestBigInteger.cpp +++ b/Tests/LibCrypto/TestBigInteger.cpp @@ -377,69 +377,6 @@ TEST_CASE(test_bigint_modular_power_extra_tests) } } -TEST_CASE(test_bigint_primality_test) -{ - struct { - Crypto::UnsignedBigInteger candidate; - bool expected_result; - } primality_tests[] = { - { "1180591620717411303424"_bigint, false }, // 2**70 - { "620448401733239439360000"_bigint, false }, // 25! - { "953962166440690129601298432"_bigint, false }, // 12**25 - { "620448401733239439360000"_bigint, false }, // 25! - { "147926426347074375"_bigint, false }, // 35! / 2**32 - { "340282366920938429742726440690708343523"_bigint, false }, // 2 factors near 2^64 - { "73"_bigint, true }, - { "6967"_bigint, true }, - { "787649"_bigint, true }, - { "73513949"_bigint, true }, - { "6691236901"_bigint, true }, - { "741387182759"_bigint, true }, - { "67466615915827"_bigint, true }, - { "9554317039214687"_bigint, true }, - { "533344522150170391"_bigint, true }, - { "18446744073709551557"_bigint, true }, // just below 2**64 - }; - - for (auto test_case : primality_tests) { - bool actual_result = Crypto::NumberTheory::is_probably_prime(test_case.candidate); - EXPECT_EQ(test_case.expected_result, actual_result); - } -} - -TEST_CASE(test_bigint_random_number_generation) -{ - struct { - Crypto::UnsignedBigInteger min; - Crypto::UnsignedBigInteger max; - } random_number_tests[] = { - { "1"_bigint, "1000000"_bigint }, - { "10000000000"_bigint, "20000000000"_bigint }, - { "1000"_bigint, "200000000000000000"_bigint }, - { "200000000000000000"_bigint, "200000000000010000"_bigint }, - }; - - for (auto test_case : random_number_tests) { - auto actual_result = Crypto::NumberTheory::random_number(test_case.min, test_case.max); - EXPECT(!(actual_result < test_case.min)); - EXPECT(actual_result < test_case.max); - } -} - -TEST_CASE(test_bigint_random_distribution) -{ - auto actual_result = Crypto::NumberTheory::random_number( - "1"_bigint, - "100000000000000000000000000000"_bigint); // 10**29 - if (actual_result < "100000000000000000000"_bigint) { // 10**20 - FAIL("Too small"); - outln("The generated number {} is extremely small. This *can* happen by pure chance, but should happen only once in a billion times. So it's probably an error.", MUST(actual_result.to_base(10))); - } else if ("99999999900000000000000000000"_bigint < actual_result) { // 10**29 - 10**20 - FAIL("Too large"); - outln("The generated number {} is extremely large. This *can* happen by pure chance, but should happen only once in a billion times. So it's probably an error.", MUST(actual_result.to_base(10))); - } -} - TEST_CASE(test_bigint_import_big_endian_decode_encode_roundtrip) { u8 random_bytes[128];