From 46eadeb81ec672ffb41dea1ec1c6380d3c38c2eb Mon Sep 17 00:00:00 2001 From: Anton Paymyshev Date: Mon, 18 Nov 2024 17:09:49 +0700 Subject: [PATCH] Fix UNSAFE_TODO for wallet [part 2 of N] (#26469) --- .../browser/eip1559_transaction.cc | 3 +- .../browser/eip2930_transaction.cc | 3 +- .../brave_wallet/browser/eth_abi_decoder.cc | 44 ++--- .../brave_wallet/browser/eth_abi_decoder.h | 3 +- .../browser/eth_allowance_manager.cc | 3 +- .../brave_wallet/browser/eth_data_parser.cc | 11 +- .../browser/eth_topics_builder.cc | 3 +- .../brave_wallet/browser/eth_transaction.cc | 5 +- .../brave_wallet/browser/ethereum_keyring.cc | 12 +- .../browser/ethereum_provider_impl.cc | 3 +- .../brave_wallet/browser/internal/hd_key.cc | 11 +- .../browser/zcash/zcash_keyring.cc | 3 +- components/brave_wallet/common/eth_address.cc | 27 +-- components/brave_wallet/common/eth_address.h | 2 +- .../brave_wallet/common/eth_request_helper.cc | 5 +- .../common/eth_sign_typed_data_helper.cc | 182 +++++++++--------- .../common/eth_sign_typed_data_helper.h | 27 +-- components/brave_wallet/common/hash_utils.cc | 40 +--- components/brave_wallet/common/hash_utils.h | 16 +- .../common/hash_utils_unittest.cc | 5 +- components/brave_wallet/common/hex_utils.cc | 4 - components/brave_wallet/common/hex_utils.h | 2 +- .../brave_wallet/common/hex_utils_unittest.cc | 9 +- components/brave_wallet/common/zcash_utils.cc | 18 +- 24 files changed, 205 insertions(+), 236 deletions(-) diff --git a/components/brave_wallet/browser/eip1559_transaction.cc b/components/brave_wallet/browser/eip1559_transaction.cc index 9541436c8e2d..cb6444c66fc1 100644 --- a/components/brave_wallet/browser/eip1559_transaction.cc +++ b/components/brave_wallet/browser/eip1559_transaction.cc @@ -9,6 +9,7 @@ #include #include +#include "base/containers/to_vector.h" #include "base/values.h" #include "brave/components/brave_wallet/browser/rlp_encode.h" #include "brave/components/brave_wallet/common/hash_utils.h" @@ -289,7 +290,7 @@ std::vector Eip1559Transaction::GetMessageToSign(uint256_t chain_id, const std::string rlp_msg = RLPEncode(base::Value(std::move(list))); result.insert(result.end(), rlp_msg.begin(), rlp_msg.end()); - return hash ? KeccakHash(result) : result; + return hash ? base::ToVector(KeccakHash(result)) : result; } std::string Eip1559Transaction::GetSignedTransaction() const { diff --git a/components/brave_wallet/browser/eip2930_transaction.cc b/components/brave_wallet/browser/eip2930_transaction.cc index e3b4e6440691..b68c26b354f6 100644 --- a/components/brave_wallet/browser/eip2930_transaction.cc +++ b/components/brave_wallet/browser/eip2930_transaction.cc @@ -8,6 +8,7 @@ #include #include +#include "base/containers/to_vector.h" #include "base/values.h" #include "brave/components/brave_wallet/browser/rlp_encode.h" #include "brave/components/brave_wallet/common/eth_address.h" @@ -179,7 +180,7 @@ std::vector Eip2930Transaction::GetMessageToSign(uint256_t chain_id, const std::string rlp_msg = RLPEncode(base::Value(std::move(list))); result.insert(result.end(), rlp_msg.begin(), rlp_msg.end()); - return hash ? KeccakHash(result) : result; + return hash ? base::ToVector(KeccakHash(result)) : result; } std::string Eip2930Transaction::GetSignedTransaction() const { diff --git a/components/brave_wallet/browser/eth_abi_decoder.cc b/components/brave_wallet/browser/eth_abi_decoder.cc index 54600861041b..9971ed12ffac 100644 --- a/components/brave_wallet/browser/eth_abi_decoder.cc +++ b/components/brave_wallet/browser/eth_abi_decoder.cc @@ -3,16 +3,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at https://mozilla.org/MPL/2.0/. */ -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(https://github.com/brave/brave-browser/issues/41661): Remove this and -// convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "brave/components/brave_wallet/browser/eth_abi_decoder.h" #include -#include #include #include #include @@ -98,8 +91,7 @@ std::optional> GetAddressFromData(ByteView input) { } return DecoderResult( - base::Value("0x" + HexEncodeLower(input.data() + kWordSize - kAddressSize, - kAddressSize)), + base::Value(ToHex(input.first(kWordSize).last(kAddressSize))), GetSubByteView(input, kWordSize), kWordSize); } @@ -116,10 +108,12 @@ std::optional> GetUintFromData(ByteView input) { return std::nullopt; } - auto arg = HexEncodeLower(input.data(), kWordSize); - uint256_t value; - if (!HexValueToUint256("0x" + arg, &value)) { + // TODO(apaymyshev): we don't need string in this bytes->string->bytes + // conversion here. + if (!HexValueToUint256( + base::StrCat({"0x", HexEncodeLower(input.first(kWordSize))}), + &value)) { return std::nullopt; } @@ -208,7 +202,7 @@ std::optional> GetBytesHexFromData( size_t parts_size = parts_count * kWordSize; return DecoderResult( - base::Value("0x" + HexEncodeLower(remaining.data(), size)), + base::Value(base::StrCat({"0x", HexEncodeLower(remaining.first(size))})), GetSubByteView(remaining, parts_size), consumed + parts_size); } @@ -474,37 +468,35 @@ std::optional> UniswapEncodedPathDecode( if (!PrefixedHexStringToBytes(encoded_path, &data)) { return std::nullopt; } - size_t offset = 0; std::vector path; + auto reader = base::SpanReader(base::as_byte_span(data)); + // The path should be long enough to encode a single-hop swap. // 43 = 20(address) + 3(fee) + 20(address) - if (data.size() < 43) { + if (reader.remaining() < 43) { return std::nullopt; } // Parse first hop address. - path.push_back("0x" + HexEncodeLower(data.data(), 20)); - offset += 20; + path.push_back(base::StrCat({"0x", HexEncodeLower(*reader.Read(20u))})); while (true) { - if (offset == data.size()) { + if (!reader.remaining()) { break; } // Parse the pool fee, and ignore. - if (data.size() - offset < 3) { + if (!reader.Skip(3u)) { return std::nullopt; } - offset += 3; - // Parse next hop. - if (data.size() - offset < 20) { + if (auto address = reader.Read(20u)) { + path.push_back(base::StrCat({"0x", HexEncodeLower(*address)})); + } else { return std::nullopt; } - path.push_back("0x" + HexEncodeLower(data.data() + offset, 20)); - offset += 20; } // Require a minimum of 2 addresses for a single-hop swap. @@ -516,9 +508,7 @@ std::optional> UniswapEncodedPathDecode( } std::optional ABIDecode(const eth_abi::Type& type, - const ByteArray& data) { - ByteView input = base::make_span(data.data(), data.size()); - + base::span input) { auto decoded = DecodeParam(type, input); if (!decoded) { return std::nullopt; diff --git a/components/brave_wallet/browser/eth_abi_decoder.h b/components/brave_wallet/browser/eth_abi_decoder.h index 0b69749fd308..7f713e100de8 100644 --- a/components/brave_wallet/browser/eth_abi_decoder.h +++ b/components/brave_wallet/browser/eth_abi_decoder.h @@ -8,7 +8,6 @@ #include #include -#include #include #include "base/values.h" @@ -20,7 +19,7 @@ std::optional> UniswapEncodedPathDecode( const std::string& encoded_path); std::optional ABIDecode(const eth_abi::Type& type, - const std::vector& data); + base::span input); } // namespace brave_wallet diff --git a/components/brave_wallet/browser/eth_allowance_manager.cc b/components/brave_wallet/browser/eth_allowance_manager.cc index 78693c31b7fb..c29bffb06f47 100644 --- a/components/brave_wallet/browser/eth_allowance_manager.cc +++ b/components/brave_wallet/browser/eth_allowance_manager.cc @@ -164,7 +164,8 @@ void EthAllowanceManager::OnGetCurrentBlock( return; } - const auto approval_topic_hash = KeccakHash(kApprovalTopicFunctionSignature); + const auto approval_topic_hash = ToHex(KeccakHash( + base::byte_span_from_cstring(kApprovalTopicFunctionSignature))); for (const auto& account_address : account_addresses) { std::string account_address_hex; if (!PadHexEncodedParameter(account_address, &account_address_hex)) { diff --git a/components/brave_wallet/browser/eth_data_parser.cc b/components/brave_wallet/browser/eth_data_parser.cc index aefb3d2627fe..7d220d863d7f 100644 --- a/components/brave_wallet/browser/eth_data_parser.cc +++ b/components/brave_wallet/browser/eth_data_parser.cc @@ -266,10 +266,10 @@ std::optional SquidDecodeCall(const base::Value::List& call) { return std::nullopt; } - std::vector calldata(calldata_with_selector->begin() + 4, - calldata_with_selector->end()); + auto [selector_span, calldata] = + base::span(*calldata_with_selector).split_at(4); - auto selector = "0x" + HexEncodeLower(calldata_with_selector->data(), 4); + auto selector = ToHex(selector_span); if (selector == kSquidExactInputSingle) { // exactInputSingle((address tokenIn, @@ -544,8 +544,9 @@ GetTransactionInfoFromData(const std::vector& data) { std::vector(), nullptr); } - std::string selector = "0x" + HexEncodeLower(data.data(), 4); - std::vector calldata(data.begin() + 4, data.end()); + auto [selector_span, calldata] = base::span(data).split_at(4); + + std::string selector = ToHex(selector_span); if (selector == kFilForwarderTransferSelector) { auto type = eth_abi::Tuple().AddTupleType(eth_abi::Bytes()).build(); auto decoded = ABIDecode(type, calldata); diff --git a/components/brave_wallet/browser/eth_topics_builder.cc b/components/brave_wallet/browser/eth_topics_builder.cc index eb39c88ac80e..3d2fc3c18bf6 100644 --- a/components/brave_wallet/browser/eth_topics_builder.cc +++ b/components/brave_wallet/browser/eth_topics_builder.cc @@ -17,7 +17,8 @@ bool MakeAssetDiscoveryTopics( const std::vector& to_account_addresses, base::Value::List* topics) { // First topic matches full keccak hash of the erc20::Transfer event signature - topics->Append(brave_wallet::KeccakHash("Transfer(address,address,uint256)")); + topics->Append(ToHex(KeccakHash( + base::byte_span_from_cstring("Transfer(address,address,uint256)")))); // Second topic matches everything (any from_address) topics->Append(base::Value()); diff --git a/components/brave_wallet/browser/eth_transaction.cc b/components/brave_wallet/browser/eth_transaction.cc index 5c23c6de2b8d..9757814b21ea 100644 --- a/components/brave_wallet/browser/eth_transaction.cc +++ b/components/brave_wallet/browser/eth_transaction.cc @@ -9,6 +9,7 @@ #include #include "base/base64.h" +#include "base/containers/to_vector.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" @@ -189,7 +190,7 @@ std::vector EthTransaction::GetMessageToSign(uint256_t chain_id, const std::string message = RLPEncode(base::Value(std::move(list))); auto result = std::vector(message.begin(), message.end()); - return hash ? KeccakHash(result) : result; + return hash ? base::ToVector(KeccakHash(result)) : result; } std::string EthTransaction::GetSignedTransaction() const { @@ -202,7 +203,7 @@ std::string EthTransaction::GetTransactionHash() const { DCHECK(IsSigned()); DCHECK(nonce_); - return KeccakHash(RLPEncode(Serialize())); + return ToHex(KeccakHash(base::as_byte_span(RLPEncode(Serialize())))); } bool EthTransaction::ProcessVRS(const std::vector& v, diff --git a/components/brave_wallet/browser/ethereum_keyring.cc b/components/brave_wallet/browser/ethereum_keyring.cc index def6cd757fb0..15066d207cd5 100644 --- a/components/brave_wallet/browser/ethereum_keyring.cc +++ b/components/brave_wallet/browser/ethereum_keyring.cc @@ -8,7 +8,10 @@ #include #include "base/base64.h" +#include "base/containers/extend.h" #include "base/containers/span.h" +#include "base/containers/to_vector.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "brave/components/brave_wallet/browser/eth_transaction.h" #include "brave/components/brave_wallet/common/eth_address.h" @@ -20,12 +23,11 @@ namespace { // Get the 32 byte message hash std::vector GetMessageHash(base::span message) { - std::string prefix("\x19"); - prefix += std::string("Ethereum Signed Message:\n" + - base::NumberToString(message.size())); + std::string prefix = base::StrCat({"\x19", "Ethereum Signed Message:\n", + base::NumberToString(message.size())}); std::vector hash_input(prefix.begin(), prefix.end()); - hash_input.insert(hash_input.end(), message.begin(), message.end()); - return brave_wallet::KeccakHash(hash_input); + base::Extend(hash_input, message); + return base::ToVector(KeccakHash(hash_input)); } } // namespace diff --git a/components/brave_wallet/browser/ethereum_provider_impl.cc b/components/brave_wallet/browser/ethereum_provider_impl.cc index 552a7679dc65..ee2b9c8ea45d 100644 --- a/components/brave_wallet/browser/ethereum_provider_impl.cc +++ b/components/brave_wallet/browser/ethereum_provider_impl.cc @@ -12,6 +12,7 @@ #include #include "base/containers/contains.h" +#include "base/containers/to_vector.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/strings/strcat.h" @@ -685,7 +686,7 @@ void EthereumProviderImpl::SignTypedMessage( mojom::SignDataUnion::NewEthSignTypedData(std::move(eth_sign_typed_data)); SignMessageInternal(account_id, std::move(sign_data), - std::move(message_to_sign), std::move(callback), + base::ToVector(message_to_sign), std::move(callback), std::move(id)); } diff --git a/components/brave_wallet/browser/internal/hd_key.cc b/components/brave_wallet/browser/internal/hd_key.cc index 4efa32a57bb7..9ff922674ba4 100644 --- a/components/brave_wallet/browser/internal/hd_key.cc +++ b/components/brave_wallet/browser/internal/hd_key.cc @@ -16,6 +16,7 @@ #include "base/check.h" #include "base/containers/span.h" +#include "base/containers/to_vector.h" #include "base/json/json_reader.h" #include "base/logging.h" #include "base/numerics/byte_conversions.h" @@ -25,6 +26,7 @@ #include "base/strings/string_util.h" #include "brave/components/brave_wallet/common/bitcoin_utils.h" #include "brave/components/brave_wallet/common/hash_utils.h" +#include "brave/components/brave_wallet/common/hex_utils.h" #include "brave/components/brave_wallet/common/zcash_utils.h" #include "brave/third_party/bitcoin-core/src/src/base58.h" #include "brave/vendor/bat-native-tweetnacl/tweetnacl.h" @@ -78,8 +80,7 @@ bool UTCPasswordVerification(const std::string& derived_key, mac_verification_input.insert(mac_verification_input.end(), ciphertext.begin(), ciphertext.end()); // verify password - std::vector mac_verification(KeccakHash(mac_verification_input)); - if (base::ToLowerASCII(base::HexEncode(mac_verification)) != mac) { + if (HexEncodeLower(KeccakHash(mac_verification_input)) != mac) { VLOG(0) << __func__ << ": password does not match"; return false; } @@ -397,7 +398,7 @@ void HDKey::SetPrivateKey(base::span value) { } private_key_.assign(value.begin(), value.end()); GeneratePublicKey(); - identifier_ = Hash160(public_key_); + identifier_ = base::ToVector(Hash160(public_key_)); const uint8_t* ptr = identifier_.data(); fingerprint_ = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3] << 0; @@ -425,8 +426,8 @@ void HDKey::SetPublicKey( LOG(ERROR) << __func__ << ": not a valid public key"; return; } - public_key_.assign(value.begin(), value.end()); - identifier_ = Hash160(public_key_); + public_key_ = base::ToVector(value); + identifier_ = base::ToVector(Hash160(public_key_)); const uint8_t* ptr = identifier_.data(); fingerprint_ = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3] << 0; diff --git a/components/brave_wallet/browser/zcash/zcash_keyring.cc b/components/brave_wallet/browser/zcash/zcash_keyring.cc index 1c8ab747eaa8..81fc7508a983 100644 --- a/components/brave_wallet/browser/zcash/zcash_keyring.cc +++ b/components/brave_wallet/browser/zcash/zcash_keyring.cc @@ -9,6 +9,7 @@ #include #include "base/check.h" +#include "base/containers/to_vector.h" #include "brave/components/brave_wallet/common/common_utils.h" #include "brave/components/brave_wallet/common/hash_utils.h" #include "brave/components/brave_wallet/common/zcash_utils.h" @@ -72,7 +73,7 @@ std::optional> ZCashKeyring::GetPubkeyHash( return std::nullopt; } - return Hash160(hd_key_base->GetPublicKeyBytes()); + return base::ToVector(Hash160(hd_key_base->GetPublicKeyBytes())); } #if BUILDFLAG(ENABLE_ORCHARD) diff --git a/components/brave_wallet/common/eth_address.cc b/components/brave_wallet/common/eth_address.cc index 055f65737905..2ef19dc05089 100644 --- a/components/brave_wallet/common/eth_address.cc +++ b/components/brave_wallet/common/eth_address.cc @@ -3,19 +3,14 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at https://mozilla.org/MPL/2.0/. */ -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(https://github.com/brave/brave-browser/issues/41661): Remove this and -// convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "brave/components/brave_wallet/common/eth_address.h" #include -#include "base/check_op.h" +#include "base/containers/span.h" #include "base/logging.h" #include "base/ranges/algorithm.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "brave/components/brave_wallet/common/hash_utils.h" @@ -39,7 +34,8 @@ bool EthAddress::operator!=(const EthAddress& other) const { } // static -EthAddress EthAddress::FromPublicKey(const std::vector& public_key) { +EthAddress EthAddress::FromPublicKey(base::span public_key) { + // TODO(apaymyshev): should be a fixed-size span. if (public_key.size() != 64) { VLOG(1) << __func__ << ": public key size should be 64 bytes"; return EthAddress(); @@ -91,8 +87,7 @@ bool EthAddress::IsValidAddress(const std::string& input) { } std::string EthAddress::ToHex() const { - const std::string input(bytes_.begin(), bytes_.end()); - return ::brave_wallet::ToHex(input); + return ::brave_wallet::ToHex(bytes_); } // static @@ -117,21 +112,19 @@ std::optional EthAddress::ToEip1191ChecksumAddress( std::string EthAddress::ToChecksumAddress(uint256_t eip1191_chaincode) const { std::string result = "0x"; - std::string input; + std::string prefix; if (eip1191_chaincode == static_cast(30) || eip1191_chaincode == static_cast(31)) { // TODO(jocelyn): We will need to revise this if there are supported chains // with ID larger than uint64_t. - input += + prefix = base::NumberToString(static_cast(eip1191_chaincode)) + "0x"; } - input += std::string(ToHex().data() + 2); - - const std::string hash_str(KeccakHash(input).data() + 2); - const std::string address_str = - base::ToLowerASCII(base::HexEncode(bytes_.data(), bytes_.size())); + const std::string address_str = HexEncodeLower(bytes_); + const std::string hash_str = base::HexEncode( + KeccakHash(base::as_byte_span(base::StrCat({prefix, address_str})))); for (size_t i = 0; i < address_str.length(); ++i) { if (isdigit(address_str[i])) { diff --git a/components/brave_wallet/common/eth_address.h b/components/brave_wallet/common/eth_address.h index 1ec46ff55c1e..a4c89dfd1c43 100644 --- a/components/brave_wallet/common/eth_address.h +++ b/components/brave_wallet/common/eth_address.h @@ -20,7 +20,7 @@ class EthAddress { public: // public key must be uncompressed and no header byte so its length is 64 // bytes - static EthAddress FromPublicKey(const std::vector& public_key); + static EthAddress FromPublicKey(base::span public_key); // input should be a valid address with 20 bytes hex representation starting // with 0x static EthAddress FromHex(const std::string& input); diff --git a/components/brave_wallet/common/eth_request_helper.cc b/components/brave_wallet/common/eth_request_helper.cc index e721c156bb7a..7c2a48d80318 100644 --- a/components/brave_wallet/common/eth_request_helper.cc +++ b/components/brave_wallet/common/eth_request_helper.cc @@ -12,6 +12,7 @@ #include "base/base64.h" #include "base/compiler_specific.h" +#include "base/containers/to_vector.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/ranges/algorithm.h" @@ -528,12 +529,12 @@ mojom::EthSignTypedDataPtr ParseEthSignTypedDataParams( result->meta = nullptr; } - result->domain_hash = domain_hash->first; + result->domain_hash = base::ToVector(domain_hash->first); if (!base::JSONWriter::Write(domain_hash->second, &result->domain_json)) { return nullptr; } - result->primary_hash = primary_hash->first; + result->primary_hash = base::ToVector(primary_hash->first); if (!base::JSONWriter::Write(primary_hash->second, &result->message_json)) { return nullptr; } 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 c2a40825bc56..dcbedfd7688b 100644 --- a/components/brave_wallet/common/eth_sign_typed_data_helper.cc +++ b/components/brave_wallet/common/eth_sign_typed_data_helper.cc @@ -3,16 +3,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at https://mozilla.org/MPL/2.0/. */ -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(https://github.com/brave/brave-browser/issues/41661): Remove this and -// convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "brave/components/brave_wallet/common/eth_sign_typed_data_helper.h" -#include #include +#include #include #include "base/containers/extend.h" @@ -53,7 +47,7 @@ void EthSignTypedDataHelper::SetVersion(Version version) { void EthSignTypedDataHelper::FindAllDependencyTypes( base::flat_map* known_types, - const std::string& anchor_type_name) const { + const std::string_view anchor_type_name) const { DCHECK(!anchor_type_name.empty()); DCHECK(known_types); @@ -71,9 +65,9 @@ void EthSignTypedDataHelper::FindAllDependencyTypes( } const std::string* type = field.GetDict().FindString("type"); if (type) { - auto type_split = base::SplitString(*type, "[", base::KEEP_WHITESPACE, - base::SPLIT_WANT_ALL); - std::string lookup_type = *type; + const auto type_split = base::SplitStringPiece( + *type, "[", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); + std::string_view lookup_type = *type; if (type_split.size() == 2) { lookup_type = type_split[0]; } @@ -87,7 +81,7 @@ void EthSignTypedDataHelper::FindAllDependencyTypes( std::string EthSignTypedDataHelper::EncodeType( const base::Value& type, - const std::string& type_name) const { + const std::string_view type_name) const { if (!type.is_list()) { return std::string(); } @@ -113,7 +107,7 @@ std::string EthSignTypedDataHelper::EncodeType( } std::string EthSignTypedDataHelper::EncodeTypes( - const std::string& primary_type_name) const { + const std::string_view primary_type_name) const { std::string result; base::flat_map types_map; @@ -132,15 +126,14 @@ std::string EthSignTypedDataHelper::EncodeTypes( return result; } -std::vector EthSignTypedDataHelper::GetTypeHash( - const std::string& primary_type_name) const { - const std::string type_hash = - KeccakHash(EncodeTypes(primary_type_name), false); - return std::vector(type_hash.begin(), type_hash.end()); +EthSignTypedDataHelper::Eip712HashArray EthSignTypedDataHelper::GetTypeHash( + const std::string_view primary_type_name) const { + return KeccakHash(base::as_byte_span(EncodeTypes(primary_type_name))); } -std::optional, base::Value::Dict>> -EthSignTypedDataHelper::HashStruct(const std::string& primary_type_name, +std::optional< + std::pair> +EthSignTypedDataHelper::HashStruct(const std::string_view primary_type_name, const base::Value::Dict& data) const { auto encoded_data = EncodeData(primary_type_name, data); if (!encoded_data) { @@ -153,16 +146,17 @@ EthSignTypedDataHelper::HashStruct(const std::string& primary_type_name, // Encode the json data by the its type defined in json custom types starting // from primary type. See unittests for some examples. std::optional, base::Value::Dict>> -EthSignTypedDataHelper::EncodeData(const std::string& primary_type_name, +EthSignTypedDataHelper::EncodeData(const std::string_view primary_type_name, const base::Value::Dict& data) const { const auto* primary_type = types_.FindList(primary_type_name); if (!primary_type) { return std::nullopt; } std::vector result; + // 32 bytes for type hash and for each item in schema. + result.reserve(Eip712HashArray().size() * (1 + primary_type->size())); - const std::vector type_hash = GetTypeHash(primary_type_name); - result.insert(result.end(), type_hash.begin(), type_hash.end()); + base::Extend(result, GetTypeHash(primary_type_name)); base::Value::Dict sanitized_data; @@ -179,27 +173,27 @@ EthSignTypedDataHelper::EncodeData(const std::string& primary_type_name, if (!encoded_field) { return std::nullopt; } - result.insert(result.end(), encoded_field->begin(), encoded_field->end()); + base::Extend(result, *encoded_field); sanitized_data.Set(*name_str, value->Clone()); } else { if (version_ == Version::kV4) { - for (size_t i = 0; i < 32; ++i) { - result.push_back(0); - } + // https://github.com/MetaMask/eth-sig-util/blob/66a8c0935c14d6ef80b583148d0c758c198a9c4a/src/sign-typed-data.ts#L248 + // Insert null line in case of a missing field. + result.insert(result.end(), 32, 0); } } } + return std::make_pair(result, std::move(sanitized_data)); } // Encode each field of a custom type, if a field is also a custom type it // will call EncodeData recursively until it reaches an atomic type -std::optional> EthSignTypedDataHelper::EncodeField( - const std::string& type, - const base::Value& value) const { +std::optional +EthSignTypedDataHelper::EncodeField(const std::string_view type, + const base::Value& value) const { // ES6 section 20.1.2.6 Number.MAX_SAFE_INTEGER constexpr double kMaxSafeInteger = static_cast(kMaxSafeIntegerUint64); - std::vector result; if (type.ends_with(']')) { if (version_ != Version::kV4) { @@ -209,34 +203,31 @@ std::optional> EthSignTypedDataHelper::EncodeField( if (!value.is_list()) { return std::nullopt; } - auto type_split = base::SplitString(type, "[", base::KEEP_WHITESPACE, - base::SPLIT_WANT_ALL); + const auto type_split = base::SplitStringPiece( + type, "[", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); if (type_split.size() != 2) { return std::nullopt; } - const std::string array_type = type_split[0]; std::vector array_result; for (const auto& item : value.GetList()) { - auto encoded_item = EncodeField(array_type, item); + auto encoded_item = EncodeField(type_split[0], item); if (!encoded_item) { return std::nullopt; } - array_result.insert(array_result.end(), encoded_item->begin(), - encoded_item->end()); + base::Extend(array_result, *encoded_item); } - auto array_hash = KeccakHash(array_result); - result.insert(result.end(), array_hash.begin(), array_hash.end()); - } else if (type == "string") { + return KeccakHash(array_result); + } + + if (type == "string") { const std::string* value_str = value.GetIfString(); if (!value_str) { return std::nullopt; } - const std::string encoded_value = KeccakHash(*value_str, false); - const std::vector encoded_value_bytes(encoded_value.begin(), - encoded_value.end()); - result.insert(result.end(), encoded_value_bytes.begin(), - encoded_value_bytes.end()); - } else if (type == "bytes") { + return KeccakHash(base::as_byte_span(*value_str)); + } + + if (type == "bytes") { const std::string* value_str = value.GetIfString(); if (!value_str || (!value_str->empty() && !IsValidHexString(*value_str))) { return std::nullopt; @@ -245,19 +236,21 @@ std::optional> EthSignTypedDataHelper::EncodeField( if (!value_str->empty()) { CHECK(PrefixedHexStringToBytes(*value_str, &bytes)); } - const std::vector encoded_value = KeccakHash(bytes); - result.insert(result.end(), encoded_value.begin(), encoded_value.end()); - } else if (type == "bool") { + return KeccakHash(bytes); + } + + if (type == "bool") { std::optional value_bool = value.GetIfBool(); if (!value_bool) { return std::nullopt; } - uint256_t encoded_value = (uint256_t)*value_bool; - // 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 (type == "address") { + + Eip712HashArray result = {}; + result.back() = value_bool.value() ? 1 : 0; + return result; + } + + if (type == "address") { const std::string* value_str = value.GetIfString(); if (!value_str || !IsValidHexString(*value_str)) { return std::nullopt; @@ -267,13 +260,15 @@ std::optional> EthSignTypedDataHelper::EncodeField( if (address.size() != 20u) { return std::nullopt; } - for (size_t i = 0; i < 256 - 160; i += 8) { - result.push_back(0); - } - result.insert(result.end(), address.begin(), address.end()); - } else if (type.starts_with("bytes")) { + + Eip712HashArray result = {}; + base::as_writable_byte_span(result).last(20u).copy_from(address); + return result; + } + + if (type.starts_with("bytes")) { unsigned num_bits; - if (!base::StringToUint(type.data() + 5, &num_bits) || num_bits > 32) { + if (!base::StringToUint(type.substr(5), &num_bits) || num_bits > 32) { return std::nullopt; } const std::string* value_str = value.GetIfString(); @@ -285,14 +280,15 @@ std::optional> EthSignTypedDataHelper::EncodeField( if (bytes.size() > 32) { return std::nullopt; } - result.insert(result.end(), bytes.begin(), bytes.end()); - for (size_t i = 0; i < 32u - bytes.size(); ++i) { - result.push_back(0); - } - } else if (type.starts_with("uint")) { + Eip712HashArray result = {}; + base::as_writable_byte_span(result).copy_prefix_from(bytes); + return result; + } + + if (type.starts_with("uint")) { // uint8 to uint256 in steps of 8 unsigned num_bits; - if (!base::StringToUint(type.data() + 4, &num_bits) || + if (!base::StringToUint(type.substr(4), &num_bits) || !ValidSolidityBits(num_bits)) { return std::nullopt; } @@ -324,14 +320,17 @@ std::optional> EthSignTypedDataHelper::EncodeField( return std::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 (type.starts_with("int")) { + Eip712HashArray result = {}; + base::span(result).copy_from(base::byte_span_from_ref(encoded_value)); + base::ranges::reverse(result); + + return result; + } + + if (type.starts_with("int")) { // int8 to int256 in steps of 8 unsigned num_bits; - if (!base::StringToUint(type.data() + 3, &num_bits) || + if (!base::StringToUint(type.substr(3), &num_bits) || !ValidSolidityBits(num_bits)) { return std::nullopt; } @@ -364,32 +363,32 @@ std::optional> EthSignTypedDataHelper::EncodeField( return std::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 (!value.is_dict()) { - return std::nullopt; - } - auto encoded_data = EncodeData(type, value.GetDict()); - if (!encoded_data) { - return std::nullopt; - } - std::vector encoded_value = KeccakHash(encoded_data->first); + Eip712HashArray result = {}; + base::span(result).copy_from(base::byte_span_from_ref(encoded_value)); + base::ranges::reverse(result); - result.insert(result.end(), encoded_value.begin(), encoded_value.end()); + return result; } - return result; + + if (!value.is_dict()) { + return std::nullopt; + } + auto encoded_data = EncodeData(type, value.GetDict()); + if (!encoded_data) { + return std::nullopt; + } + return KeccakHash(encoded_data->first); } -std::optional, base::Value::Dict>> +std::optional< + std::pair> EthSignTypedDataHelper::GetTypedDataDomainHash( const base::Value::Dict& domain) const { return HashStruct("EIP712Domain", domain); } -std::optional, base::Value::Dict>> +std::optional< + std::pair> EthSignTypedDataHelper::GetTypedDataPrimaryHash( const std::string& primary_type_name, const base::Value::Dict& message) const { @@ -397,7 +396,8 @@ EthSignTypedDataHelper::GetTypedDataPrimaryHash( } // static -std::vector EthSignTypedDataHelper::GetTypedDataMessageToSign( +EthSignTypedDataHelper::Eip712HashArray +EthSignTypedDataHelper::GetTypedDataMessageToSign( base::span domain_hash, base::span primary_hash) { DCHECK(!domain_hash.empty()); diff --git a/components/brave_wallet/common/eth_sign_typed_data_helper.h b/components/brave_wallet/common/eth_sign_typed_data_helper.h index f4655c33328d..97949f031b70 100644 --- a/components/brave_wallet/common/eth_sign_typed_data_helper.h +++ b/components/brave_wallet/common/eth_sign_typed_data_helper.h @@ -16,6 +16,7 @@ #include "base/containers/span.h" #include "base/gtest_prod_util.h" #include "base/values.h" +#include "brave/components/brave_wallet/common/hash_utils.h" namespace brave_wallet { @@ -23,6 +24,7 @@ namespace brave_wallet { // https://eips.ethereum.org/EIPS/eip-712 class EthSignTypedDataHelper { public: + using Eip712HashArray = KeccakHashArray; enum class Version { kV3, kV4 }; static std::unique_ptr Create(base::Value::Dict types, Version version); @@ -34,20 +36,20 @@ class EthSignTypedDataHelper { void SetTypes(base::Value::Dict types); void SetVersion(Version version); - std::vector GetTypeHash(const std::string& primary_type_name) const; - std::optional, base::Value::Dict>> HashStruct( - const std::string& primary_type_name, + Eip712HashArray GetTypeHash(const std::string_view primary_type_name) const; + std::optional> HashStruct( + const std::string_view primary_type_name, const base::Value::Dict& data) const; std::optional, base::Value::Dict>> EncodeData( - const std::string& primary_type_name, + const std::string_view primary_type_name, const base::Value::Dict& data) const; - static std::vector GetTypedDataMessageToSign( + static Eip712HashArray GetTypedDataMessageToSign( base::span domain_hash, base::span primary_hash); - std::optional, base::Value::Dict>> + std::optional> GetTypedDataPrimaryHash(const std::string& primary_type_name, const base::Value::Dict& message) const; - std::optional, base::Value::Dict>> + std::optional> GetTypedDataDomainHash(const base::Value::Dict& domain) const; private: @@ -61,14 +63,13 @@ class EthSignTypedDataHelper { void FindAllDependencyTypes( base::flat_map* known_types, - const std::string& anchor_type_name) const; + const std::string_view anchor_type_name) const; std::string EncodeType(const base::Value& type, - const std::string& type_name) const; - std::string EncodeTypes(const std::string& primary_type_name) const; + const std::string_view type_name) const; + std::string EncodeTypes(const std::string_view primary_type_name) const; - std::optional> EncodeField( - const std::string& type, - const base::Value& value) const; + std::optional EncodeField(const std::string_view type_string, + const base::Value& value) const; base::Value::Dict types_; Version version_; diff --git a/components/brave_wallet/common/hash_utils.cc b/components/brave_wallet/common/hash_utils.cc index 2ce58ec9e7e6..7bfb02ec89f3 100644 --- a/components/brave_wallet/common/hash_utils.cc +++ b/components/brave_wallet/common/hash_utils.cc @@ -8,8 +8,6 @@ #include #include -#include "base/check.h" -#include "base/compiler_specific.h" #include "base/containers/adapters.h" #include "base/containers/span.h" #include "base/ranges/algorithm.h" @@ -31,33 +29,19 @@ std::array ConcatArrays(const std::array& arr1, } } // namespace -std::string KeccakHash(const std::string& input, bool to_hex) { - std::vector bytes(input.begin(), input.end()); - std::vector result = KeccakHash(bytes); - std::string result_str(result.begin(), result.end()); - return to_hex ? ToHex(result_str) : result_str; -} - -std::vector KeccakHash(const std::vector& input) { - auto hash = ethash_keccak256(input.data(), input.size()); - return UNSAFE_TODO(std::vector(hash.bytes, hash.bytes + 32)); -} - -eth_abi::Bytes32 KeccakHashBytes32(base::span input) { +KeccakHashArray KeccakHash(base::span input) { auto hash = ethash_keccak256(input.data(), input.size()); - eth_abi::Bytes32 result; + KeccakHashArray result; static_assert(sizeof(result) == sizeof(hash.bytes)); base::ranges::copy(hash.bytes, result.begin()); return result; } std::string GetFunctionHash(const std::string& input) { - std::string result = KeccakHash(input); - return result.substr(0, std::min(static_cast(10), result.length())); + return ToHex(GetFunctionHashBytes4(input)); } eth_abi::Bytes4 GetFunctionHashBytes4(const std::string& input) { - auto full_hash = KeccakHashBytes32(base::as_bytes(base::make_span(input))); eth_abi::Bytes4 bytes_result; base::span(bytes_result) .copy_from( @@ -67,12 +51,12 @@ eth_abi::Bytes4 GetFunctionHashBytes4(const std::string& input) { eth_abi::Bytes32 Namehash(const std::string& name) { eth_abi::Bytes32 hash = {}; - std::vector labels = - SplitString(name, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + auto labels = SplitStringPiece(name, ".", base::KEEP_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); for (const auto& label : base::Reversed(labels)) { - auto label_hash = KeccakHashBytes32(base::as_bytes(base::make_span(label))); - hash = KeccakHashBytes32(ConcatArrays(hash, label_hash)); + auto label_hash = KeccakHash(base::as_byte_span(label)); + hash = KeccakHash(ConcatArrays(hash, label_hash)); } return hash; } @@ -81,15 +65,11 @@ SHA256HashArray DoubleSHA256Hash(base::span input) { return crypto::SHA256Hash(crypto::SHA256Hash(input)); } -std::vector Hash160(base::span input) { - std::vector result(CRIPEMD160::OUTPUT_SIZE); - - std::array sha256hash = - crypto::SHA256Hash(input); - DCHECK(!sha256hash.empty()); +Ripemd160HashArray Hash160(base::span input) { + Ripemd160HashArray result = {}; CRIPEMD160() - .Write(sha256hash.data(), sha256hash.size()) + .Write(crypto::SHA256Hash(input).data(), crypto::kSHA256Length) .Finalize(result.data()); return result; diff --git a/components/brave_wallet/common/hash_utils.h b/components/brave_wallet/common/hash_utils.h index 63d093548b10..2af8e272f15e 100644 --- a/components/brave_wallet/common/hash_utils.h +++ b/components/brave_wallet/common/hash_utils.h @@ -8,17 +8,20 @@ #include #include -#include #include "brave/components/brave_wallet/common/eth_abi_utils.h" #include "crypto/sha2.h" namespace brave_wallet { -// Equivalent to web3.utils.keccak256(string) -std::string KeccakHash(const std::string& input, bool to_hex = true); -std::vector KeccakHash(const std::vector& input); -eth_abi::Bytes32 KeccakHashBytes32(base::span input); +inline constexpr size_t kKeccakHashLength = 32; +inline constexpr size_t kRipemd160HashLength = 20; + +using KeccakHashArray = std::array; +using SHA256HashArray = std::array; +using Ripemd160HashArray = std::array; + +KeccakHashArray KeccakHash(base::span input); // Returns the hex encoding of the first 4 bytes of the hash. // For example: keccak('balanceOf(address)') @@ -31,11 +34,10 @@ eth_abi::Bytes4 GetFunctionHashBytes4(const std::string& input); eth_abi::Bytes32 Namehash(const std::string& name); // sha256(sha256(input)) -using SHA256HashArray = std::array; SHA256HashArray DoubleSHA256Hash(base::span input); // ripemd160(sha256(input)) -std::vector Hash160(base::span input); +Ripemd160HashArray Hash160(base::span input); } // namespace brave_wallet diff --git a/components/brave_wallet/common/hash_utils_unittest.cc b/components/brave_wallet/common/hash_utils_unittest.cc index 55da6011d88c..587b4740a162 100644 --- a/components/brave_wallet/common/hash_utils_unittest.cc +++ b/components/brave_wallet/common/hash_utils_unittest.cc @@ -7,6 +7,7 @@ #include +#include "base/containers/span.h" #include "brave/components/brave_wallet/common/hex_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -14,10 +15,10 @@ namespace brave_wallet { TEST(HashUtilsUnitTest, KeccakHash) { ASSERT_EQ( - KeccakHash(""), + ToHex(KeccakHash({})), "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); ASSERT_EQ( - KeccakHash("hello world"), + ToHex(KeccakHash(base::byte_span_from_cstring("hello world"))), "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"); } diff --git a/components/brave_wallet/common/hex_utils.cc b/components/brave_wallet/common/hex_utils.cc index 9cf037c0c645..ed543bd98d31 100644 --- a/components/brave_wallet/common/hex_utils.cc +++ b/components/brave_wallet/common/hex_utils.cc @@ -31,10 +31,6 @@ std::string ToHex(base::span data) { // Returns a hex string representation of a binary buffer. The returned hex // string will be in lower case, without the 0x prefix. -std::string HexEncodeLower(const void* bytes, size_t size) { - return base::ToLowerASCII(base::HexEncode(bytes, size)); -} - std::string HexEncodeLower(base::span bytes) { return base::ToLowerASCII(base::HexEncode(bytes)); } diff --git a/components/brave_wallet/common/hex_utils.h b/components/brave_wallet/common/hex_utils.h index e8559690f2c1..831792f8bbc7 100644 --- a/components/brave_wallet/common/hex_utils.h +++ b/components/brave_wallet/common/hex_utils.h @@ -16,12 +16,12 @@ namespace brave_wallet { // Equivalent to web3.utils.toHex(string); +// TODO(apaymyshev): rename it to To0xHex or something like that. std::string ToHex(const std::string& data); std::string ToHex(base::span data); // Returns a hex string representation of a binary buffer. The returned hex // string will be in lower case, without the 0x prefix. -std::string HexEncodeLower(const void* bytes, size_t size); std::string HexEncodeLower(base::span bytes); // Determines if the passed in hex string is valid diff --git a/components/brave_wallet/common/hex_utils_unittest.cc b/components/brave_wallet/common/hex_utils_unittest.cc index e6f445bc99f6..554e249c7602 100644 --- a/components/brave_wallet/common/hex_utils_unittest.cc +++ b/components/brave_wallet/common/hex_utils_unittest.cc @@ -9,7 +9,7 @@ #include #include -#include "base/logging.h" +#include "base/containers/span.h" #include "testing/gtest/include/gtest/gtest.h" namespace brave_wallet { @@ -18,8 +18,7 @@ TEST(HexUtilsUnitTest, ToHex) { const std::string_view str = "hello world"; ASSERT_EQ(ToHex(""), "0x0"); ASSERT_EQ(ToHex(std::string(str)), "0x68656c6c6f20776f726c64"); - ASSERT_EQ(ToHex(base::as_bytes(base::make_span(str))), - "0x68656c6c6f20776f726c64"); + ASSERT_EQ(ToHex(base::as_byte_span(str)), "0x68656c6c6f20776f726c64"); ASSERT_EQ(ToHex(std::vector()), "0x0"); ASSERT_EQ(ToHex(std::vector(str.begin(), str.end())), @@ -28,9 +27,7 @@ TEST(HexUtilsUnitTest, ToHex) { TEST(HexUtilsUnitTest, HexEncodeLower) { std::string test_string = "hello world"; - ASSERT_EQ(HexEncodeLower(base::as_bytes(base::make_span(test_string))), - "68656c6c6f20776f726c64"); - ASSERT_EQ(HexEncodeLower(test_string.data(), test_string.size()), + ASSERT_EQ(HexEncodeLower(base::as_byte_span(test_string)), "68656c6c6f20776f726c64"); } diff --git a/components/brave_wallet/common/zcash_utils.cc b/components/brave_wallet/common/zcash_utils.cc index 997ec72b76d9..e0fa83732668 100644 --- a/components/brave_wallet/common/zcash_utils.cc +++ b/components/brave_wallet/common/zcash_utils.cc @@ -10,6 +10,7 @@ #include #include +#include "base/containers/extend.h" #include "base/containers/span.h" #include "base/numerics/byte_conversions.h" #include "base/types/expected.h" @@ -39,7 +40,7 @@ std::array GetPaddedHRP(bool is_testnet) { "Wrong kPaddedHrpSize size"); std::string hrp = is_testnet ? kTestnetHRP : kMainnetHRP; std::array padded_hrp = {}; - base::ranges::copy(base::make_span(hrp), padded_hrp.begin()); + base::ranges::copy(base::as_byte_span(hrp), padded_hrp.begin()); return padded_hrp; } @@ -189,8 +190,8 @@ std::string PubkeyToTransparentAddress(base::span pubkey, bool testnet) { std::vector result = GetNetworkPrefix(testnet); - std::vector data_part = Hash160(pubkey); - result.insert(result.end(), data_part.begin(), data_part.end()); + base::Extend(result, Hash160(pubkey)); + return Base58EncodeWithCheck(result); } @@ -282,18 +283,15 @@ std::optional> ExtractParsedAddresses( return std::nullopt; } - auto padded_hrp = GetPaddedHRP(is_testnet); + auto [body, hrp] = + base::span(*reverted).split_at(reverted->size() - kPaddedHrpSize); // Check that HRP is similar to the padded HRP - if (!std::equal(padded_hrp.begin(), padded_hrp.end(), - reverted->end() - kPaddedHrpSize)) { + if (GetPaddedHRP(is_testnet) != hrp) { return std::nullopt; } - auto parts = ParseUnifiedAddressBody( - base::make_span(*reverted).subspan(0, reverted->size() - kPaddedHrpSize)); - - return parts; + return ParseUnifiedAddressBody(body); } // https://zips.z.cash/zip-0316#encoding-of-unified-addresses