From 48a3a02238a4cbb631d9bb24f93ffd19d1272b00 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Fri, 12 Jan 2024 21:34:23 +0000 Subject: [PATCH] LibCrypto: Make constructing a BigInteger from string fallible Previously, constructing a `UnsignedBigInteger::from_base()` could produce an incorrect result if the input string contained a valid Base36 digit that was out of range of the given base. The same method would also crash if the input string contained an invalid Base36 digit. An error is now returned in both these cases. Constructing a BigFraction from string is now also fallible, so that we can handle the case where we are given an input string with invalid digits. --- Tests/LibCrypto/TestBigInteger.cpp | 64 ++++++++++--------- Userland/Applications/Calculator/main.cpp | 5 +- .../LibCrypto/BigFraction/BigFraction.cpp | 10 ++- .../LibCrypto/BigFraction/BigFraction.h | 5 +- .../LibCrypto/BigInt/SignedBigInteger.cpp | 6 +- .../LibCrypto/BigInt/SignedBigInteger.h | 4 +- .../LibCrypto/BigInt/UnsignedBigInteger.cpp | 12 +++- .../LibCrypto/BigInt/UnsignedBigInteger.h | 4 +- .../Libraries/LibJS/Bytecode/ASTCodegen.cpp | 8 +-- Userland/Libraries/LibJS/Runtime/Value.cpp | 4 +- .../LibWeb/HTML/StructuredSerialize.cpp | 3 +- 11 files changed, 68 insertions(+), 57 deletions(-) diff --git a/Tests/LibCrypto/TestBigInteger.cpp b/Tests/LibCrypto/TestBigInteger.cpp index 92e3e03194..25ff9ad0ec 100644 --- a/Tests/LibCrypto/TestBigInteger.cpp +++ b/Tests/LibCrypto/TestBigInteger.cpp @@ -234,9 +234,13 @@ TEST_CASE(test_unsigned_bigint_division_combined_test) TEST_CASE(test_unsigned_bigint_base10_from_string) { - auto result = Crypto::UnsignedBigInteger::from_base(10, "57195071295721390579057195715793"sv); + auto result = TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(10, "57195071295721390579057195715793"sv)); Vector expected_result { 3806301393, 954919431, 3879607298, 721 }; EXPECT_EQ(result.words(), expected_result); + + Vector invalid_base10_number_strings { "1A"sv, "1:"sv, "Z1"sv, "1/"sv }; + for (auto invalid_base10_number_string : invalid_base10_number_strings) + EXPECT_EQ(Crypto::UnsignedBigInteger::from_base(10, invalid_base10_number_string).is_error(), true); } TEST_CASE(test_unsigned_bigint_base10_to_string) @@ -701,11 +705,11 @@ EXPECT_EQUAL_TO(zero, -0.0); double double_below_max_value = nextafter(double_max_value, 0.0); VERIFY(double_below_max_value < double_max_value); VERIFY(double_below_max_value < (double_max_value - 1.0)); - auto max_value_in_bigint = Crypto::SignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto max_value_in_bigint = TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)); auto max_value_plus_one = max_value_in_bigint.plus(Crypto::SignedBigInteger { 1 }); auto max_value_minus_one = max_value_in_bigint.minus(Crypto::SignedBigInteger { 1 }); - auto below_max_value_in_bigint = Crypto::SignedBigInteger::from_base(16, "fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto below_max_value_in_bigint = TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(16, "fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)); EXPECT_EQUAL_TO(max_value_in_bigint, double_max_value); EXPECT_LESS_THAN(max_value_minus_one, double_max_value); @@ -723,11 +727,11 @@ EXPECT_EQUAL_TO(zero, -0.0); double double_above_min_value = nextafter(double_min_value, 0.0); VERIFY(double_above_min_value > double_min_value); VERIFY(double_above_min_value > (double_min_value + 1.0)); - auto min_value_in_bigint = Crypto::SignedBigInteger::from_base(16, "-fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto min_value_in_bigint = TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(16, "-fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)); auto min_value_plus_one = min_value_in_bigint.plus(Crypto::SignedBigInteger { 1 }); auto min_value_minus_one = min_value_in_bigint.minus(Crypto::SignedBigInteger { 1 }); - auto above_min_value_in_bigint = Crypto::SignedBigInteger::from_base(16, "-fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto above_min_value_in_bigint = TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(16, "-fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)); EXPECT_EQUAL_TO(min_value_in_bigint, double_min_value); EXPECT_LESS_THAN(min_value_minus_one, double_min_value); @@ -785,89 +789,89 @@ TEST_CASE(to_double) double infinity = INFINITY; EXPECT_TO_EQUAL_DOUBLE( - Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)), double_max_value); EXPECT_TO_EQUAL_DOUBLE( - Crypto::UnsignedBigInteger::from_base(16, "ffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "ffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)), double_max_value); EXPECT_TO_EQUAL_DOUBLE( - Crypto::UnsignedBigInteger::from_base(16, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"sv), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"sv)), double_max_value); EXPECT_TO_EQUAL_DOUBLE( - Crypto::UnsignedBigInteger::from_base(16, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)), infinity); EXPECT_TO_EQUAL_DOUBLE( - Crypto::SignedBigInteger::from_base(16, "-fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(16, "-fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)), -double_max_value); EXPECT_TO_EQUAL_DOUBLE( - Crypto::SignedBigInteger::from_base(16, "-ffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(16, "-ffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)), -double_max_value); EXPECT_TO_EQUAL_DOUBLE( - Crypto::SignedBigInteger::from_base(16, "-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"sv), + TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(16, "-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"sv)), -double_max_value); EXPECT_TO_EQUAL_DOUBLE( - Crypto::SignedBigInteger::from_base(16, "-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(16, "-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)), -infinity); EXPECT_TO_EQUAL_DOUBLE( - Crypto::UnsignedBigInteger::from_base(16, "ffffffffffffffff"sv), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "ffffffffffffffff"sv)), 18446744073709549568.0); EXPECT_TO_EQUAL_DOUBLE( - Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff800"sv), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff800"sv)), 18446744073709549568.0); EXPECT_TO_EQUAL_DOUBLE( - Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff8ff"sv), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff8ff"sv)), 18446744073709549568.0); - EXPECT_TO_EQUAL_DOUBLE(Crypto::SignedBigInteger::from_base(10, "1234567890123456789"sv), + EXPECT_TO_EQUAL_DOUBLE(TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(10, "1234567890123456789"sv)), 1234567890123456800.0); - EXPECT_TO_EQUAL_DOUBLE(Crypto::SignedBigInteger::from_base(10, "2345678901234567890"sv), + EXPECT_TO_EQUAL_DOUBLE(TRY_OR_FAIL(Crypto::SignedBigInteger::from_base(10, "2345678901234567890"sv)), 2345678901234567680.0); EXPECT_EQ( - Crypto::UnsignedBigInteger::from_base(16, "1fffffffffffff00"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "1fffffffffffff00"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), 2305843009213693696.0); EXPECT_EQ( - Crypto::UnsignedBigInteger::from_base(16, "1fffffffffffff00"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::RoundTowardZero), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "1fffffffffffff00"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::RoundTowardZero), 2305843009213693696.0); EXPECT_EQ( - Crypto::UnsignedBigInteger::from_base(16, "1fffffffffffff80"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), + TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "1fffffffffffff80"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), 2305843009213693952.0); - EXPECT_EQ(Crypto::UnsignedBigInteger::from_base(16, "20000000000001"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), + EXPECT_EQ(TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "20000000000001"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), 9007199254740992.0); - EXPECT_EQ(Crypto::UnsignedBigInteger::from_base(16, "20000000000002"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), + EXPECT_EQ(TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "20000000000002"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), 9007199254740994.0); // 2^53 = 20000000000000, +3 Rounds up because of tiesRoundToEven - EXPECT_EQ(Crypto::UnsignedBigInteger::from_base(16, "20000000000003"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), + EXPECT_EQ(TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "20000000000003"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), 9007199254740996.0); // +4 is exactly 9007199254740996 - EXPECT_EQ(Crypto::UnsignedBigInteger::from_base(16, "20000000000004"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), + EXPECT_EQ(TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "20000000000004"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), 9007199254740996.0); // +5 rounds down because of tiesRoundToEven - EXPECT_EQ(Crypto::UnsignedBigInteger::from_base(16, "20000000000005"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), + EXPECT_EQ(TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "20000000000005"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), 9007199254740996.0); - EXPECT_EQ(Crypto::UnsignedBigInteger::from_base(16, "20000000000006"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), + EXPECT_EQ(TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "20000000000006"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), 9007199254740998.0); - EXPECT_EQ(Crypto::UnsignedBigInteger::from_base(10, "98382635059784269824"sv).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), + EXPECT_EQ(TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(10, "98382635059784269824"sv)).to_double(Crypto::UnsignedBigInteger::RoundingMode::IEEERoundAndTiesToEvenMantissa), bit_cast(0x4415555555555555ULL)); #undef EXPECT_TO_EQUAL_DOUBLE @@ -982,11 +986,11 @@ TEST_CASE(unsigned_bigint_double_comparisons) double double_below_max_value = nextafter(double_max_value, 0.0); VERIFY(double_below_max_value < double_max_value); VERIFY(double_below_max_value < (double_max_value - 1.0)); - auto max_value_in_bigint = Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto max_value_in_bigint = TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)); auto max_value_plus_one = max_value_in_bigint.plus(Crypto::UnsignedBigInteger { 1 }); auto max_value_minus_one = max_value_in_bigint.minus(Crypto::UnsignedBigInteger { 1 }); - auto below_max_value_in_bigint = Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto below_max_value_in_bigint = TRY_OR_FAIL(Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv)); EXPECT_EQUAL_TO(max_value_in_bigint, double_max_value); EXPECT_LESS_THAN(max_value_minus_one, double_max_value); diff --git a/Userland/Applications/Calculator/main.cpp b/Userland/Applications/Calculator/main.cpp index 246ea6acb4..1796f767f9 100644 --- a/Userland/Applications/Calculator/main.cpp +++ b/Userland/Applications/Calculator/main.cpp @@ -59,8 +59,9 @@ ErrorOr serenity_main(Main::Arguments arguments) auto clipboard = GUI::Clipboard::the().fetch_data_and_type(); if (clipboard.mime_type == "text/plain") { if (!clipboard.data.is_empty()) { - auto const number = StringView(clipboard.data); - widget->set_typed_entry(Crypto::BigFraction(number)); + auto number_or_error = Crypto::BigFraction::from_string(StringView(clipboard.data)); + if (!number_or_error.is_error()) + widget->set_typed_entry(number_or_error.release_value()); } } })); diff --git a/Userland/Libraries/LibCrypto/BigFraction/BigFraction.cpp b/Userland/Libraries/LibCrypto/BigFraction/BigFraction.cpp index 97c2382def..40c6972682 100644 --- a/Userland/Libraries/LibCrypto/BigFraction/BigFraction.cpp +++ b/Userland/Libraries/LibCrypto/BigFraction/BigFraction.cpp @@ -25,20 +25,18 @@ BigFraction::BigFraction(SignedBigInteger value) { } -BigFraction::BigFraction(StringView sv) +ErrorOr BigFraction::from_string(StringView sv) { - // FIXME: This constructor is definitely fallible, errors should also be propagated - // from both signed and unsigned version of from_base. auto maybe_dot_index = sv.find('.'); auto integer_part_view = sv.substring_view(0, maybe_dot_index.value_or(sv.length())); auto fraction_part_view = maybe_dot_index.has_value() ? sv.substring_view(1 + *maybe_dot_index) : "0"sv; - auto integer_part = SignedBigInteger::from_base(10, integer_part_view); - auto fractional_part = SignedBigInteger::from_base(10, fraction_part_view); + auto integer_part = TRY(SignedBigInteger::from_base(10, integer_part_view)); + auto fractional_part = TRY(SignedBigInteger::from_base(10, fraction_part_view)); auto fraction_length = UnsignedBigInteger(static_cast(fraction_part_view.length())); - *this = BigFraction(move(integer_part)) + BigFraction(move(fractional_part), NumberTheory::Power("10"_bigint, move(fraction_length))); + return BigFraction(move(integer_part)) + BigFraction(move(fractional_part), NumberTheory::Power("10"_bigint, move(fraction_length))); } BigFraction BigFraction::operator+(BigFraction const& rhs) const diff --git a/Userland/Libraries/LibCrypto/BigFraction/BigFraction.h b/Userland/Libraries/LibCrypto/BigFraction/BigFraction.h index 682fe9431d..aaa18a85c2 100644 --- a/Userland/Libraries/LibCrypto/BigFraction/BigFraction.h +++ b/Userland/Libraries/LibCrypto/BigFraction/BigFraction.h @@ -14,7 +14,7 @@ namespace Crypto { class BigFraction { // FIXME Make the whole API more error-friendly. This includes: // - Propagating errors from BigIntegers - // - Returns errors from both BigFraction(StringView) and BigFraction(numerator, denominator); + // - Returns errors from BigFraction(numerator, denominator); // - Duplicate fallible operators with a error-friendly version public: @@ -27,9 +27,10 @@ public: BigFraction& operator=(Crypto::BigFraction const&) = default; BigFraction& operator=(Crypto::BigFraction&&) = default; - explicit BigFraction(StringView); explicit BigFraction(double); + static ErrorOr from_string(StringView); + BigFraction operator+(BigFraction const&) const; BigFraction operator-(BigFraction const&) const; BigFraction operator*(BigFraction const&) const; diff --git a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp index 1aca97dfc3..14b70a8c39 100644 --- a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp @@ -35,7 +35,7 @@ size_t SignedBigInteger::export_data(Bytes data, bool remove_leading_zeros) cons return m_unsigned_data.export_data(bytes_view, remove_leading_zeros) + 1; } -SignedBigInteger SignedBigInteger::from_base(u16 N, StringView str) +ErrorOr SignedBigInteger::from_base(u16 N, StringView str) { auto sign = false; if (str.length() > 1) { @@ -47,8 +47,8 @@ SignedBigInteger SignedBigInteger::from_base(u16 N, StringView str) if (maybe_sign == '+') str = str.substring_view(1); } - auto unsigned_data = UnsignedBigInteger::from_base(N, str); - return { move(unsigned_data), sign }; + auto unsigned_data = TRY(UnsignedBigInteger::from_base(N, str)); + return SignedBigInteger { move(unsigned_data), sign }; } ErrorOr SignedBigInteger::to_base(u16 N) const diff --git a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h index f6100d8bc5..532bc7ed46 100644 --- a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h +++ b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h @@ -63,7 +63,7 @@ public: size_t export_data(Bytes, bool remove_leading_zeros = false) const; - [[nodiscard]] static SignedBigInteger from_base(u16 N, StringView str); + [[nodiscard]] static ErrorOr from_base(u16 N, StringView str); [[nodiscard]] ErrorOr to_base(u16 N) const; [[nodiscard]] ByteString to_base_deprecated(u16 N) const; @@ -171,5 +171,5 @@ struct AK::Formatter : AK::Formatter UnsignedBigInteger::from_base(u16 N, StringView str) { VERIFY(N <= 36); UnsignedBigInteger result; UnsignedBigInteger base { N }; - for (auto& c : str) { + for (auto const& c : str) { if (c == '_') continue; - result = result.multiplied_by(base).plus(parse_ascii_base36_digit(c)); + if (!is_ascii_base36_digit(c)) + return Error::from_string_literal("Invalid Base36 digit"); + auto digit = parse_ascii_base36_digit(c); + if (digit >= N) + return Error::from_string_literal("Base36 digit out of range"); + + result = result.multiplied_by(base).plus(digit); } return result; } diff --git a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index 2df4652ec5..ac817f7a93 100644 --- a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -63,7 +63,7 @@ public: size_t export_data(Bytes, bool remove_leading_zeros = false) const; - [[nodiscard]] static UnsignedBigInteger from_base(u16 N, StringView str); + [[nodiscard]] static ErrorOr from_base(u16 N, StringView str); [[nodiscard]] ErrorOr to_base(u16 N) const; [[nodiscard]] ByteString to_base_deprecated(u16 N) const; @@ -161,5 +161,5 @@ struct AK::Formatter : Formatter { inline Crypto::UnsignedBigInteger operator""_bigint(char const* string, size_t length) { - return Crypto::UnsignedBigInteger::from_base(10, { string, length }); + return MUST(Crypto::UnsignedBigInteger::from_base(10, { string, length })); } diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 6706599c69..db4b5da00c 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -273,12 +273,12 @@ Bytecode::CodeGenerationErrorOr BigIntLiteral::generate_bytecode(Bytecode: auto integer = [&] { if (m_value[0] == '0' && m_value.length() >= 3) if (m_value[1] == 'x' || m_value[1] == 'X') - return Crypto::SignedBigInteger::from_base(16, m_value.substring(2, m_value.length() - 3)); + return MUST(Crypto::SignedBigInteger::from_base(16, m_value.substring(2, m_value.length() - 3))); if (m_value[1] == 'o' || m_value[1] == 'O') - return Crypto::SignedBigInteger::from_base(8, m_value.substring(2, m_value.length() - 3)); + return MUST(Crypto::SignedBigInteger::from_base(8, m_value.substring(2, m_value.length() - 3))); if (m_value[1] == 'b' || m_value[1] == 'B') - return Crypto::SignedBigInteger::from_base(2, m_value.substring(2, m_value.length() - 3)); - return Crypto::SignedBigInteger::from_base(10, m_value.substring(0, m_value.length() - 1)); + return MUST(Crypto::SignedBigInteger::from_base(2, m_value.substring(2, m_value.length() - 3))); + return MUST(Crypto::SignedBigInteger::from_base(10, m_value.substring(0, m_value.length() - 1))); }(); generator.emit(integer); diff --git a/Userland/Libraries/LibJS/Runtime/Value.cpp b/Userland/Libraries/LibJS/Runtime/Value.cpp index 7046483ead..077d7b4254 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.cpp +++ b/Userland/Libraries/LibJS/Runtime/Value.cpp @@ -673,7 +673,7 @@ double string_to_number(StringView string) // 4. Return StringNumericValue of literal. if (result->base != 10) { - auto bigint = Crypto::UnsignedBigInteger::from_base(result->base, result->literal); + auto bigint = MUST(Crypto::UnsignedBigInteger::from_base(result->base, result->literal)); return bigint.to_double(); } @@ -842,7 +842,7 @@ static Optional string_to_bigint(VM& vm, StringView string) // 4. Let mv be the MV of literal. // 5. Assert: mv is an integer. - auto bigint = Crypto::SignedBigInteger::from_base(result->base, result->literal); + auto bigint = MUST(Crypto::SignedBigInteger::from_base(result->base, result->literal)); if (result->is_negative && (bigint != BIGINT_ZERO)) bigint.negate(); diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp index 4febb9e293..ca1d70021d 100644 --- a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp +++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp @@ -936,7 +936,8 @@ private: auto string_view = TRY(Bindings::throw_dom_exception_if_needed(vm, [&string]() { return string->utf8_string_view(); })); - return JS::BigInt::create(vm, ::Crypto::SignedBigInteger::from_base(10, string_view.substring_view(0, string_view.length() - 1))); + auto bigint = MUST(::Crypto::SignedBigInteger::from_base(10, string_view.substring_view(0, string_view.length() - 1))); + return JS::BigInt::create(vm, bigint); } };