diff --git a/Userland/Utilities/test-crypto.cpp b/Userland/Utilities/test-crypto.cpp index 42f954c1d4..5c3749ed57 100644 --- a/Userland/Utilities/test-crypto.cpp +++ b/Userland/Utilities/test-crypto.cpp @@ -590,7 +590,6 @@ static void rsa_test_encrypt(); static void rsa_test_der_parse(); static void rsa_test_encrypt_decrypt(); static void rsa_emsa_pss_test_create(); -static void bigint_test_number_theory(); // FIXME: we should really move these num theory stuff out static void tls_test_client_hello(); @@ -603,6 +602,11 @@ static void bigint_base10(); static void bigint_import_export(); static void bigint_bitwise(); +static void bigint_theory_modular_inverse(); +static void bigint_theory_modular_power(); +static void bigint_theory_primality(); +static void bigint_theory_random_number(); + static void bigint_test_signed_fibo500(); static void bigint_signed_addition_edgecases(); static void bigint_signed_subtraction(); @@ -1783,7 +1787,6 @@ static int rsa_tests() { rsa_test_encrypt(); rsa_test_der_parse(); - bigint_test_number_theory(); rsa_test_encrypt_decrypt(); rsa_emsa_pss_test_create(); return g_some_test_failed ? 1 : 0; @@ -1831,124 +1834,6 @@ static void rsa_test_encrypt() } } -static void bigint_test_number_theory() -{ - { - I_TEST((Number Theory | Modular Inverse)); - if (Crypto::NumberTheory::ModularInverse(7, 87) == 25) { - PASS; - } else { - FAIL(Invalid result); - } - } - { - struct { - Crypto::UnsignedBigInteger base; - Crypto::UnsignedBigInteger exp; - Crypto::UnsignedBigInteger mod; - Crypto::UnsignedBigInteger expected; - } mod_pow_tests[] = { - { "2988348162058574136915891421498819466320163312926952423791023078876139"_bigint, "2351399303373464486466122544523690094744975233415544072992656881240319"_bigint, "10000"_bigint, "3059"_bigint }, - { "24231"_bigint, "12448"_bigint, "14679"_bigint, "4428"_bigint }, - { "1005404"_bigint, "8352654"_bigint, "8161408"_bigint, "2605696"_bigint }, - { "3665005778"_bigint, "3244425589"_bigint, "565668506"_bigint, "524766494"_bigint }, - { "10662083169959689657"_bigint, "11605678468317533000"_bigint, "1896834583057209739"_bigint, "1292743154593945858"_bigint }, - { "99667739213529524852296932424683448520"_bigint, "123394910770101395416306279070921784207"_bigint, "238026722756504133786938677233768788719"_bigint, "197165477545023317459748215952393063201"_bigint }, - { "49368547511968178788919424448914214709244872098814465088945281575062739912239"_bigint, "25201856190991298572337188495596990852134236115562183449699512394891190792064"_bigint, "45950460777961491021589776911422805972195170308651734432277141467904883064645"_bigint, "39917885806532796066922509794537889114718612292469285403012781055544152450051"_bigint }, - { "48399385336454791246880286907257136254351739111892925951016159217090949616810"_bigint, "5758661760571644379364752528081901787573279669668889744323710906207949658569"_bigint, "32812120644405991429173950312949738783216437173380339653152625840449006970808"_bigint, "7948464125034399875323770213514649646309423451213282653637296324080400293584"_bigint }, - }; - - for (auto test_case : mod_pow_tests) { - I_TEST((Number Theory | Modular Power)); - auto actual = Crypto::NumberTheory::ModularPower( - test_case.base, test_case.exp, test_case.mod); - - if (actual == test_case.expected) { - PASS; - } else { - FAIL(Wrong result); - printf("b: %s\ne: %s\nm: %s\nexpect: %s\nactual: %s\n", - test_case.base.to_base10().characters(), test_case.exp.to_base10().characters(), test_case.mod.to_base10().characters(), test_case.expected.to_base10().characters(), actual.to_base10().characters()); - } - } - } - { - 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) { - I_TEST((Number Theory | Primality)); - bool actual_result = Crypto::NumberTheory::is_probably_prime(test_case.candidate); - if (test_case.expected_result == actual_result) { - PASS; - } else { - FAIL(Wrong primality guess); - printf("The number %s is %sa prime, but the test said it is %sa prime!\n", - test_case.candidate.to_base10().characters(), test_case.expected_result ? "" : "not ", actual_result ? "" : "not "); - } - } - } - { - struct { - Crypto::UnsignedBigInteger min; - Crypto::UnsignedBigInteger max; - } primality_tests[] = { - { "1"_bigint, "1000000"_bigint }, - { "10000000000"_bigint, "20000000000"_bigint }, - { "1000"_bigint, "200000000000000000"_bigint }, - { "200000000000000000"_bigint, "200000000000010000"_bigint }, - }; - - for (auto test_case : primality_tests) { - I_TEST((Number Theory | Random numbers)); - auto actual_result = Crypto::NumberTheory::random_number(test_case.min, test_case.max); - if (actual_result < test_case.min) { - FAIL(Too small); - printf("The generated number %s is smaller than the requested minimum %s. (max = %s)\n", actual_result.to_base10().characters(), test_case.min.to_base10().characters(), test_case.max.to_base10().characters()); - } else if (!(actual_result < test_case.max)) { - FAIL(Too large); - printf("The generated number %s is larger-or-equal to the requested maximum %s. (min = %s)\n", actual_result.to_base10().characters(), test_case.max.to_base10().characters(), test_case.min.to_base10().characters()); - } else { - PASS; - } - } - } - { - I_TEST((Number Theory | 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); - printf("The generated number %s is extremely small. This *can* happen by pure chance, but should happen only once in a billion times. So it's probably an error.\n", actual_result.to_base10().characters()); - } else if ("99999999900000000000000000000"_bigint < actual_result) { // 10**29 - 10**20 - FAIL(Too large); - printf("The generated number %s is extremely large. This *can* happen by pure chance, but should happen only once in a billion times. So it's probably an error.\n", actual_result.to_base10().characters()); - } else { - PASS; - } - } -} - static void rsa_emsa_pss_test_create() { { @@ -2170,6 +2055,11 @@ static int bigint_tests() bigint_import_export(); bigint_bitwise(); + bigint_theory_modular_inverse(); + bigint_theory_modular_power(); + bigint_theory_primality(); + bigint_theory_random_number(); + bigint_test_signed_fibo500(); bigint_signed_addition_edgecases(); bigint_signed_subtraction(); @@ -2502,6 +2392,198 @@ static void bigint_base10() } } +static void bigint_theory_modular_inverse() +{ + { + I_TEST((Number Theory | Modular Inverse)); + if (Crypto::NumberTheory::ModularInverse(7, 87) == 25) { + PASS; + } else { + FAIL(Invalid result); + } + } +} + +static void bigint_theory_modular_power() +{ + { + I_TEST((BigInteger | Simple Modular Power | Even)); + Crypto::UnsignedBigInteger base { 7 }; + Crypto::UnsignedBigInteger exponent { 2 }; + Crypto::UnsignedBigInteger modulo { 10 }; + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + if (result.words() == Vector { 9 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Simple Modular Power | Odd)); + Crypto::UnsignedBigInteger base { 10 }; + Crypto::UnsignedBigInteger exponent { 2 }; + Crypto::UnsignedBigInteger modulo { 9 }; + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + if (result.words() == Vector { 1 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Large Modular Power | Even Fibonacci)); + Crypto::UnsignedBigInteger base = bigint_fibonacci(200); + Crypto::UnsignedBigInteger exponent = bigint_fibonacci(100); + Crypto::UnsignedBigInteger modulo = bigint_fibonacci(150); + // Result according to Wolfram Alpha : 7195284628716783672927396027925 + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + if (result.words() == Vector { 2042093077, 1351416233, 3510104665, 90 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Large Modular Power | Odd Fibonacci)); + Crypto::UnsignedBigInteger base = bigint_fibonacci(200); + Crypto::UnsignedBigInteger exponent = bigint_fibonacci(100); + Crypto::UnsignedBigInteger modulo = bigint_fibonacci(149); + // Result according to Wolfram Alpha : 1136278609611966596838389694992 + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + if (result.words() == Vector { 2106049040, 2169509253, 1468244710, 14 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + { + I_TEST((BigInteger | Large Modular Power | Odd Fibonacci with carry)); + Crypto::UnsignedBigInteger base = bigint_fibonacci(200); + Crypto::UnsignedBigInteger exponent = bigint_fibonacci(100); + Crypto::UnsignedBigInteger modulo = bigint_fibonacci(185); + // Result according to Wolfram Alpha : 55094573983071006678665780782730672080 + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + if (result.words() == Vector { 1988720592, 2097784252, 347129583, 695391288 }) { + PASS; + } else { + FAIL(Incorrect Result); + } + } + + { + struct { + Crypto::UnsignedBigInteger base; + Crypto::UnsignedBigInteger exp; + Crypto::UnsignedBigInteger mod; + Crypto::UnsignedBigInteger expected; + } mod_pow_tests[] = { + { "2988348162058574136915891421498819466320163312926952423791023078876139"_bigint, "2351399303373464486466122544523690094744975233415544072992656881240319"_bigint, "10000"_bigint, "3059"_bigint }, + { "24231"_bigint, "12448"_bigint, "14679"_bigint, "4428"_bigint }, + { "1005404"_bigint, "8352654"_bigint, "8161408"_bigint, "2605696"_bigint }, + { "3665005778"_bigint, "3244425589"_bigint, "565668506"_bigint, "524766494"_bigint }, + { "10662083169959689657"_bigint, "11605678468317533000"_bigint, "1896834583057209739"_bigint, "1292743154593945858"_bigint }, + { "99667739213529524852296932424683448520"_bigint, "123394910770101395416306279070921784207"_bigint, "238026722756504133786938677233768788719"_bigint, "197165477545023317459748215952393063201"_bigint }, + { "49368547511968178788919424448914214709244872098814465088945281575062739912239"_bigint, "25201856190991298572337188495596990852134236115562183449699512394891190792064"_bigint, "45950460777961491021589776911422805972195170308651734432277141467904883064645"_bigint, "39917885806532796066922509794537889114718612292469285403012781055544152450051"_bigint }, + { "48399385336454791246880286907257136254351739111892925951016159217090949616810"_bigint, "5758661760571644379364752528081901787573279669668889744323710906207949658569"_bigint, "32812120644405991429173950312949738783216437173380339653152625840449006970808"_bigint, "7948464125034399875323770213514649646309423451213282653637296324080400293584"_bigint }, + }; + + for (auto test_case : mod_pow_tests) { + I_TEST((BigInteger | Modular Power | Several other test cases)); + auto actual = Crypto::NumberTheory::ModularPower( + test_case.base, test_case.exp, test_case.mod); + + if (actual == test_case.expected) { + PASS; + } else { + FAIL(Wrong result); + printf("b: %s\ne: %s\nm: %s\nexpect: %s\nactual: %s\n", + test_case.base.to_base10().characters(), test_case.exp.to_base10().characters(), test_case.mod.to_base10().characters(), test_case.expected.to_base10().characters(), actual.to_base10().characters()); + } + } + } +} + +static void bigint_theory_primality() +{ + 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) { + I_TEST((BigInteger | Primality)); + bool actual_result = Crypto::NumberTheory::is_probably_prime(test_case.candidate); + if (test_case.expected_result == actual_result) { + PASS; + } else { + FAIL(Wrong primality guess); + printf("The number %s is %sa prime, but the test said it is %sa prime!\n", + test_case.candidate.to_base10().characters(), test_case.expected_result ? "" : "not ", actual_result ? "" : "not "); + } + } +} + +static void bigint_theory_random_number() +{ + { + 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) { + I_TEST((BigInteger | Random numbers)); + auto actual_result = Crypto::NumberTheory::random_number(test_case.min, test_case.max); + if (actual_result < test_case.min) { + FAIL(Too small); + printf("The generated number %s is smaller than the requested minimum %s. (max = %s)\n", actual_result.to_base10().characters(), test_case.min.to_base10().characters(), test_case.max.to_base10().characters()); + } else if (!(actual_result < test_case.max)) { + FAIL(Too large); + printf("The generated number %s is larger-or-equal to the requested maximum %s. (min = %s)\n", actual_result.to_base10().characters(), test_case.max.to_base10().characters(), test_case.min.to_base10().characters()); + } else { + PASS; + } + } + } + { + I_TEST((BigInteger | 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); + printf("The generated number %s is extremely small. This *can* happen by pure chance, but should happen only once in a billion times. So it's probably an error.\n", actual_result.to_base10().characters()); + } else if ("99999999900000000000000000000"_bigint < actual_result) { // 10**29 - 10**20 + FAIL(Too large); + printf("The generated number %s is extremely large. This *can* happen by pure chance, but should happen only once in a billion times. So it's probably an error.\n", actual_result.to_base10().characters()); + } else { + PASS; + } + } +} + static void bigint_import_export() { {