Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support [u]intN; 0 < N <=256, N%8 == 0 for typed data #12958

Merged
merged 1 commit into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions components/brave_wallet/common/brave_wallet_types.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint256_t> 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<int256_t> 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<int256_t> 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
11 changes: 11 additions & 0 deletions components/brave_wallet/common/brave_wallet_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ constexpr int128_t kMin128BitInt = std::numeric_limits<int128_t>::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<uint256_t> MaxSolidityUint(size_t bits);
absl::optional<int256_t> MaxSolidityInt(size_t bits);
absl::optional<int256_t> MinSolidityInt(size_t bits);

struct TransactionReceipt {
TransactionReceipt();
~TransactionReceipt();
Expand Down
39 changes: 39 additions & 0 deletions components/brave_wallet/common/brave_wallet_types_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<size_t> 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<uint128_t>::max()));
EXPECT_EQ(MaxSolidityUint(256),
uint256_t(std::numeric_limits<uint256_t>::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
86 changes: 18 additions & 68 deletions components/brave_wallet/common/eth_sign_typed_data_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ absl::optional<std::vector<uint8_t>> 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))
Expand All @@ -241,9 +241,9 @@ absl::optional<std::vector<uint8_t>> 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<double> value_double = value.GetIfDouble();
Expand All @@ -262,44 +262,20 @@ absl::optional<std::vector<uint8_t>> EthSignTypedDataHelper::EncodeField(
return absl::nullopt;
}

// check if value excceeds type bound
switch (type_check) {
case 8:
if (encoded_value > std::numeric_limits<uint8_t>::max())
return absl::nullopt;
break;
case 16:
if (encoded_value > std::numeric_limits<uint16_t>::max())
return absl::nullopt;
break;
case 32:
if (encoded_value > std::numeric_limits<uint32_t>::max())
return absl::nullopt;
break;
case 64:
if (encoded_value > std::numeric_limits<uint64_t>::max())
return absl::nullopt;
break;
case 128:
if (encoded_value > std::numeric_limits<uint128_t>::max())
return absl::nullopt;
break;
case 256:
if (encoded_value > std::numeric_limits<uint256_t>::max())
return absl::nullopt;
break;
default:
return absl::nullopt;
absl::optional<uint256_t> 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<uint8_t>((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<double> value_double = value.GetIfDouble();
const std::string* value_str = value.GetIfString();
Expand All @@ -317,39 +293,13 @@ absl::optional<std::vector<uint8_t>> EthSignTypedDataHelper::EncodeField(
return absl::nullopt;
}

// check if value excceeds type bound
switch (type_check) {
case 8:
if (encoded_value > std::numeric_limits<int8_t>::max() ||
encoded_value < std::numeric_limits<int8_t>::min())
return absl::nullopt;
break;
case 16:
if (encoded_value > std::numeric_limits<int16_t>::max() ||
encoded_value < std::numeric_limits<int16_t>::min())
return absl::nullopt;
break;
case 32:
if (encoded_value > std::numeric_limits<int32_t>::max() ||
encoded_value < std::numeric_limits<int32_t>::min())
return absl::nullopt;
break;
case 64:
if (encoded_value > std::numeric_limits<int64_t>::max() ||
encoded_value < std::numeric_limits<int64_t>::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<int256_t> min_value = MinSolidityInt(num_bits);
absl::optional<int256_t> 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<uint8_t>((encoded_value >> i) & 0xFF));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -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");
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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");
Expand Down