From b451cd524e607a2d856c3e274e79678398feeba0 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Thu, 7 Apr 2022 17:37:58 -0400 Subject: [PATCH] Support uintN; 0 < N <=256, N%8 == 0 for typed data --- .../brave_wallet/common/brave_wallet_types.cc | 44 ++++++++++ .../brave_wallet/common/brave_wallet_types.h | 11 +++ .../common/brave_wallet_types_unittest.cc | 39 +++++++++ .../common/eth_sign_typed_data_helper.cc | 86 ++++--------------- .../eth_sign_typed_data_helper_unittest.cc | 25 ++++++ 5 files changed, 137 insertions(+), 68 deletions(-) diff --git a/components/brave_wallet/common/brave_wallet_types.cc b/components/brave_wallet/common/brave_wallet_types.cc index 4ace0f9e4562..a773f478c056 100644 --- a/components/brave_wallet/common/brave_wallet_types.cc +++ b/components/brave_wallet/common/brave_wallet_types.cc @@ -104,4 +104,48 @@ bool SolanaAccountInfo::operator!=(const SolanaAccountInfo& info) const { return !operator==(info); } +bool ValidSolidityBits(size_t bits) { + return bits != 0 && bits % 8 == 0 && bits <= 256; +} + +absl::optional MaxSolidityUint(size_t bits) { + if (!ValidSolidityBits(bits)) + return absl::nullopt; + // Max hex for intN value is 0x[ff]... for num bytes + uint256_t value = 0; + const size_t num_bytes = bits / 8; + for (size_t i = 0; i < num_bytes; i++) { + value <<= 8; + value += 0xff; + } + return value; +} + +absl::optional MaxSolidityInt(size_t bits) { + if (!ValidSolidityBits(bits)) + return absl::nullopt; + // Max hex for intN value is 0x7f[ff]... for num bytes - 1 + int256_t value = 0x7f; + const size_t num_bytes = bits / 8; + for (size_t i = 0; i < num_bytes - 1; i++) { + value <<= 8; + value += 0xff; + } + return value; +} + +absl::optional MinSolidityInt(size_t bits) { + if (!ValidSolidityBits(bits)) + return absl::nullopt; + // Min hex for intN value is 0x80[00]... for num bytes - 1 + // A simple bit shift doesn't work quite right because of + // using boost's int256_t type. + int256_t value = 0x80 * -1; + const size_t num_bytes = bits / 8; + for (size_t i = 0; i < num_bytes - 1; i++) { + value *= 256; + } + return value; +} + } // namespace brave_wallet diff --git a/components/brave_wallet/common/brave_wallet_types.h b/components/brave_wallet/common/brave_wallet_types.h index 1963cf7f4113..db2504cab023 100644 --- a/components/brave_wallet/common/brave_wallet_types.h +++ b/components/brave_wallet/common/brave_wallet_types.h @@ -40,6 +40,17 @@ constexpr int128_t kMin128BitInt = std::numeric_limits::min() >> 1; constexpr uint64_t kMaxSafeIntegerUint64 = 9007199254740991; // 2^53-1 +// Determines the min/max value for Solidity types such as uint56 +// uintN where 0 < N <= 256; N % 8 == 0 +// Note that you shouldn't use uint256_t and int256_t in general +// for passing around values that need to be capped in those ranges. +// This is being used for sign typed data where values are not passed +// around. +bool ValidSolidityBits(size_t bits); +absl::optional MaxSolidityUint(size_t bits); +absl::optional MaxSolidityInt(size_t bits); +absl::optional MinSolidityInt(size_t bits); + struct TransactionReceipt { TransactionReceipt(); ~TransactionReceipt(); diff --git a/components/brave_wallet/common/brave_wallet_types_unittest.cc b/components/brave_wallet/common/brave_wallet_types_unittest.cc index 877b5ccde59e..4abbff0c1be5 100644 --- a/components/brave_wallet/common/brave_wallet_types_unittest.cc +++ b/components/brave_wallet/common/brave_wallet_types_unittest.cc @@ -88,4 +88,43 @@ TEST(BraveWalletTypesTest, SolanaSignatureStatusToValue) { EXPECT_EQ(*status_from_value, status); } +TEST(BraveWalletTypesTest, ValidSolidityBits) { + for (size_t i = 8; i <= 256; i += 8) { + EXPECT_TRUE(ValidSolidityBits(i)); + } + std::vector invalid_num_bits = {0, 7, 257}; + for (size_t i : invalid_num_bits) { + EXPECT_FALSE(ValidSolidityBits(i)); + EXPECT_EQ(MaxSolidityUint(i), absl::nullopt); + EXPECT_EQ(MaxSolidityInt(i), absl::nullopt); + EXPECT_EQ(MinSolidityInt(i), absl::nullopt); + } +} + +TEST(BraveWalletTypesTest, MaxSolidityUint) { + EXPECT_EQ(MaxSolidityUint(8), uint256_t(255)); + EXPECT_EQ(MaxSolidityUint(16), uint256_t(65535)); + EXPECT_EQ(MaxSolidityUint(24), uint256_t(16777215)); + EXPECT_EQ(MaxSolidityUint(128), + uint256_t(std::numeric_limits::max())); + EXPECT_EQ(MaxSolidityUint(256), + uint256_t(std::numeric_limits::max())); +} + +TEST(BraveWalletTypesTest, MaxSolidityInt) { + EXPECT_EQ(MaxSolidityInt(8), int256_t(127)); + EXPECT_EQ(MaxSolidityInt(16), int256_t(32767)); + EXPECT_EQ(MaxSolidityInt(24), int256_t(8388607)); + EXPECT_EQ(MaxSolidityInt(128), int256_t(kMax128BitInt)); + EXPECT_EQ(MaxSolidityInt(256), int256_t(kMax256BitInt)); +} + +TEST(BraveWalletTypesTest, MinSolidityInt) { + EXPECT_EQ(MinSolidityInt(8), int256_t(-128)); + EXPECT_EQ(MinSolidityInt(16), int256_t(-32768)); + EXPECT_EQ(MinSolidityInt(24), int256_t(-8388608)); + EXPECT_EQ(MinSolidityInt(128), int256_t(kMin128BitInt)); + EXPECT_EQ(MinSolidityInt(256), int256_t(kMin256BitInt)); +} + } // namespace brave_wallet diff --git a/components/brave_wallet/common/eth_sign_typed_data_helper.cc b/components/brave_wallet/common/eth_sign_typed_data_helper.cc index dc045080ba4e..0ffdd3e868dc 100644 --- a/components/brave_wallet/common/eth_sign_typed_data_helper.cc +++ b/components/brave_wallet/common/eth_sign_typed_data_helper.cc @@ -225,8 +225,8 @@ absl::optional> EthSignTypedDataHelper::EncodeField( result.push_back(0); result.insert(result.end(), address.begin(), address.end()); } else if (base::StartsWith(type, "bytes", base::CompareCase::SENSITIVE)) { - unsigned type_check; - if (!base::StringToUint(type.data() + 5, &type_check) || type_check > 32) + unsigned num_bits; + if (!base::StringToUint(type.data() + 5, &num_bits) || num_bits > 32) return absl::nullopt; const std::string* value_str = value.GetIfString(); if (!value_str || !IsValidHexString(*value_str)) @@ -241,9 +241,9 @@ absl::optional> EthSignTypedDataHelper::EncodeField( } } else if (base::StartsWith(type, "uint", base::CompareCase::SENSITIVE)) { // uint8 to uint256 in steps of 8 - unsigned type_check; - if (!base::StringToUint(type.data() + 4, &type_check) || type_check % 8 || - type_check > 256) + unsigned num_bits; + if (!base::StringToUint(type.data() + 4, &num_bits) || + !ValidSolidityBits(num_bits)) return absl::nullopt; absl::optional value_double = value.GetIfDouble(); @@ -262,44 +262,20 @@ absl::optional> EthSignTypedDataHelper::EncodeField( return absl::nullopt; } - // check if value excceeds type bound - switch (type_check) { - case 8: - if (encoded_value > std::numeric_limits::max()) - return absl::nullopt; - break; - case 16: - if (encoded_value > std::numeric_limits::max()) - return absl::nullopt; - break; - case 32: - if (encoded_value > std::numeric_limits::max()) - return absl::nullopt; - break; - case 64: - if (encoded_value > std::numeric_limits::max()) - return absl::nullopt; - break; - case 128: - if (encoded_value > std::numeric_limits::max()) - return absl::nullopt; - break; - case 256: - if (encoded_value > std::numeric_limits::max()) - return absl::nullopt; - break; - default: - return absl::nullopt; + absl::optional max_value = MaxSolidityUint(num_bits); + if (max_value == absl::nullopt || encoded_value > *max_value) { + return absl::nullopt; } + // Append the encoded value to byte array result in big endian order for (int i = 256 - 8; i >= 0; i -= 8) { result.push_back(static_cast((encoded_value >> i) & 0xFF)); } } else if (base::StartsWith(type, "int", base::CompareCase::SENSITIVE)) { // int8 to int256 in steps of 8 - unsigned type_check; - if (!base::StringToUint(type.data() + 3, &type_check) || type_check % 8 || - type_check > 256) + unsigned num_bits; + if (!base::StringToUint(type.data() + 3, &num_bits) || + !ValidSolidityBits(num_bits)) return absl::nullopt; absl::optional value_double = value.GetIfDouble(); const std::string* value_str = value.GetIfString(); @@ -317,39 +293,13 @@ absl::optional> EthSignTypedDataHelper::EncodeField( return absl::nullopt; } - // check if value excceeds type bound - switch (type_check) { - case 8: - if (encoded_value > std::numeric_limits::max() || - encoded_value < std::numeric_limits::min()) - return absl::nullopt; - break; - case 16: - if (encoded_value > std::numeric_limits::max() || - encoded_value < std::numeric_limits::min()) - return absl::nullopt; - break; - case 32: - if (encoded_value > std::numeric_limits::max() || - encoded_value < std::numeric_limits::min()) - return absl::nullopt; - break; - case 64: - if (encoded_value > std::numeric_limits::max() || - encoded_value < std::numeric_limits::min()) - return absl::nullopt; - break; - case 128: - if (encoded_value > kMax128BitInt || encoded_value < kMin128BitInt) - return absl::nullopt; - break; - case 256: - if (encoded_value > kMax256BitInt || encoded_value < kMin256BitInt) - return absl::nullopt; - break; - default: - return absl::nullopt; + absl::optional min_value = MinSolidityInt(num_bits); + absl::optional max_value = MaxSolidityInt(num_bits); + if (min_value == absl::nullopt || max_value == absl::nullopt || + encoded_value > *max_value || encoded_value < *min_value) { + return absl::nullopt; } + // Append the encoded value to byte array result in big endian order for (int i = 256 - 8; i >= 0; i -= 8) { result.push_back(static_cast((encoded_value >> i) & 0xFF)); diff --git a/components/brave_wallet/common/eth_sign_typed_data_helper_unittest.cc b/components/brave_wallet/common/eth_sign_typed_data_helper_unittest.cc index 07bfc34ad061..80f16c9c2ff4 100644 --- a/components/brave_wallet/common/eth_sign_typed_data_helper_unittest.cc +++ b/components/brave_wallet/common/eth_sign_typed_data_helper_unittest.cc @@ -405,6 +405,7 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) { EXPECT_FALSE(helper->EncodeField("uint1", base::Value(1))); EXPECT_FALSE(helper->EncodeField("uint9", base::Value(1))); EXPECT_FALSE(helper->EncodeField("uint264", base::Value(1))); + EXPECT_FALSE(helper->EncodeField("uint55", base::Value(1))); // exceeds 8 bits maximum EXPECT_FALSE(helper->EncodeField("uint8", base::Value(256))); // exceeds Number.MAX_SAFE_INTEGER @@ -421,6 +422,13 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) { EXPECT_EQ( base::ToLowerASCII(base::HexEncode(*encoded_field)), "0000000000000000000000000000000000000000000000000000000000001000"); + + encoded_field = helper->EncodeField("uint56", base::Value(4096)); + ASSERT_TRUE(encoded_field); + EXPECT_EQ( + base::ToLowerASCII(base::HexEncode(*encoded_field)), + "0000000000000000000000000000000000000000000000000000000000001000"); + encoded_field = helper->EncodeField("uint256", base::Value(65536)); ASSERT_TRUE(encoded_field); EXPECT_EQ( @@ -437,6 +445,11 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) { "00000000000000000000000000000000000000000000000000000000000000ff"); encoded_field = helper->EncodeField("uint32", base::Value("4096")); ASSERT_TRUE(encoded_field); + EXPECT_EQ( + base::ToLowerASCII(base::HexEncode(*encoded_field)), + "0000000000000000000000000000000000000000000000000000000000001000"); + encoded_field = helper->EncodeField("uint56", base::Value("4096")); + ASSERT_TRUE(encoded_field); EXPECT_EQ( base::ToLowerASCII(base::HexEncode(*encoded_field)), "0000000000000000000000000000000000000000000000000000000000001000"); @@ -468,6 +481,8 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) { EXPECT_FALSE(helper->EncodeField("uint16", base::Value("0x10000"))); EXPECT_FALSE(helper->EncodeField("uint32", base::Value("4294967296"))); EXPECT_FALSE(helper->EncodeField("uint32", base::Value("0x100000000"))); + EXPECT_FALSE( + helper->EncodeField("uint56", base::Value("0x100000000000000"))); EXPECT_FALSE( helper->EncodeField("uint64", base::Value("18446744073709551616"))); EXPECT_FALSE( @@ -511,6 +526,7 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) { EXPECT_FALSE(helper->EncodeField("int1", base::Value(1))); EXPECT_FALSE(helper->EncodeField("int9", base::Value(1))); EXPECT_FALSE(helper->EncodeField("int264", base::Value(1))); + EXPECT_FALSE(helper->EncodeField("int55", base::Value(1))); // exceeds 8 bits maximum EXPECT_FALSE(helper->EncodeField("int8", base::Value(128))); // exceeds Number.MAX_SAFE_INTEGER @@ -576,6 +592,10 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) { EXPECT_FALSE(helper->EncodeField("int32", base::Value("2147483648"))); EXPECT_FALSE(helper->EncodeField("int32", base::Value("-2147483649"))); EXPECT_FALSE(helper->EncodeField("int32", base::Value("0x100000000"))); + EXPECT_FALSE( + helper->EncodeField("int56", base::Value("72057594037927935"))); + EXPECT_FALSE( + helper->EncodeField("int56", base::Value("-72057594037927936"))); EXPECT_FALSE( helper->EncodeField("int64", base::Value("9223372036854775808"))); EXPECT_FALSE( @@ -630,6 +650,11 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) { "000000000000000000000000000000000000000000000000000000000000007f"); encoded_field = helper->EncodeField("int32", base::Value("4096")); ASSERT_TRUE(encoded_field); + EXPECT_EQ( + base::ToLowerASCII(base::HexEncode(*encoded_field)), + "0000000000000000000000000000000000000000000000000000000000001000"); + encoded_field = helper->EncodeField("int56", base::Value("4096")); + ASSERT_TRUE(encoded_field); EXPECT_EQ( base::ToLowerASCII(base::HexEncode(*encoded_field)), "0000000000000000000000000000000000000000000000000000000000001000");