Skip to content

Sign typed data (uplift to 1.32.x) #11257

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

Merged
merged 1 commit into from
Nov 24, 2021
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
141 changes: 141 additions & 0 deletions browser/brave_wallet/brave_wallet_provider_impl_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,35 @@ class BraveWalletProviderImplUnitTest : public testing::Test {
run_loop.Run();
}

void SignTypedMessage(absl::optional<bool> user_approved,
const std::string& address,
const std::string& message,
const std::string& message_to_sign,
base::Value&& domain,
std::string* signature_out,
int* error_out,
std::string* error_message_out) {
if (!signature_out || !error_out || !error_message_out)
return;

base::RunLoop run_loop;
provider()->SignTypedMessage(
address, message, message_to_sign, std::move(domain),
base::BindLambdaForTesting([&](const std::string& signature, int error,
const std::string& error_message) {
*signature_out = signature;
*error_out = error;
*error_message_out = error_message;
run_loop.Quit();
}));
// Wait for BraveWalletProviderImpl::ContinueSignMessage
browser_task_environment_.RunUntilIdle();
if (user_approved)
brave_wallet_service_->NotifySignMessageRequestProcessed(
*user_approved, provider()->sign_message_id_ - 1);
run_loop.Run();
}

// current request id will be returned
int SignMessageRequest(const std::string& address,
const std::string& message) {
Expand Down Expand Up @@ -920,6 +949,118 @@ TEST_F(BraveWalletProviderImplUnitTest, SignMessage) {
base::ASCIIToUTF16(addresses[0])));
}

TEST_F(BraveWalletProviderImplUnitTest, SignTypedMessage) {
EXPECT_EQ(eth_json_rpc_controller()->GetChainId(), "0x1");
CreateWallet();
AddAccount();
const std::string valid_message_to_sign =
"be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2";
std::string signature;
int error;
std::string error_message;
base::Value domain(base::Value::Type::DICTIONARY);
domain.SetIntKey("chainId", 1);
SignTypedMessage(absl::nullopt, "1234", "{...}", valid_message_to_sign,
domain.Clone(), &signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kInvalidParams));
EXPECT_EQ(error_message,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));

SignTypedMessage(absl::nullopt, "0x12345678", "{...}", valid_message_to_sign,
domain.Clone(), &signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kInvalidParams));
EXPECT_EQ(error_message,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));

const std::string address = "0x1234567890123456789012345678901234567890";
// domain not dict
SignTypedMessage(absl::nullopt, address, "{...}", valid_message_to_sign,
base::Value("not dict"), &signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kInvalidParams));
EXPECT_EQ(error_message,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));

// not valid hex
SignTypedMessage(absl::nullopt, address, "{...}", "brave", domain.Clone(),
&signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kInvalidParams));
EXPECT_EQ(error_message,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));

// not valid eip712 hash
SignTypedMessage(absl::nullopt, address, "{...}", "deadbeef", domain.Clone(),
&signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kInvalidParams));
EXPECT_EQ(error_message,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));

domain.SetIntKey("chainId", 4);
std::string chain_id = "0x4";
// not active network
SignTypedMessage(absl::nullopt, address, "{...}", valid_message_to_sign,
domain.Clone(), &signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kInternalError));
EXPECT_EQ(error_message,
l10n_util::GetStringFUTF8(
IDS_BRAVE_WALLET_SIGN_TYPED_MESSAGE_CHAIN_ID_MISMATCH,
base::ASCIIToUTF16(chain_id)));
domain.SetIntKey("chainId", 1);

SignTypedMessage(absl::nullopt, address, "{...}", valid_message_to_sign,
domain.Clone(), &signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kUnauthorized));
EXPECT_EQ(error_message,
l10n_util::GetStringFUTF8(IDS_WALLET_ETH_SIGN_NOT_AUTHED,
base::ASCIIToUTF16(address)));

// No permission
const std::vector<std::string> addresses = GetAddresses();
ASSERT_FALSE(address.empty());
SignTypedMessage(absl::nullopt, addresses[0], "{...}", valid_message_to_sign,
domain.Clone(), &signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kUnauthorized));
EXPECT_EQ(error_message,
l10n_util::GetStringFUTF8(IDS_WALLET_ETH_SIGN_NOT_AUTHED,
base::ASCIIToUTF16(addresses[0])));
GURL url("https://brave.com");
Navigate(url);
AddEthereumPermission(url);
SignTypedMessage(true, addresses[0], "{...}", valid_message_to_sign,
domain.Clone(), &signature, &error, &error_message);

EXPECT_FALSE(signature.empty());
EXPECT_EQ(error, 0);
EXPECT_TRUE(error_message.empty());

// User reject request
SignTypedMessage(false, addresses[0], "{...}", valid_message_to_sign,
domain.Clone(), &signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kUserRejectedRequest));
EXPECT_EQ(error_message,
l10n_util::GetStringUTF8(IDS_WALLET_USER_REJECTED_REQUEST));

keyring_controller()->Lock();

// nullopt for the first param here because we don't AddSignMessageRequest
// whent here are no accounts returned.
SignTypedMessage(absl::nullopt, addresses[0], "{...}", valid_message_to_sign,
domain.Clone(), &signature, &error, &error_message);
EXPECT_TRUE(signature.empty());
EXPECT_EQ(error, static_cast<int>(ProviderErrors::kUnauthorized));
EXPECT_EQ(error_message,
l10n_util::GetStringFUTF8(IDS_WALLET_ETH_SIGN_NOT_AUTHED,
base::ASCIIToUTF16(addresses[0])));
}

TEST_F(BraveWalletProviderImplUnitTest, SignMessageRequestQueue) {
CreateWallet();
AddAccount();
Expand Down
67 changes: 55 additions & 12 deletions components/brave_wallet/browser/brave_wallet_provider_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -322,18 +322,63 @@ void BraveWalletProviderImpl::SignMessage(const std::string& address,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}

std::string message_str(message_bytes.begin(), message_bytes.end());
if (!base::IsStringUTF8(message_str))
message_str = ToHex(message_str);

// Convert to checksum address
auto checksum_address = EthAddress::FromHex(address);
GetAllowedAccounts(base::BindOnce(
&BraveWalletProviderImpl::ContinueSignMessage, weak_factory_.GetWeakPtr(),
checksum_address.ToChecksumAddress(), std::move(message_bytes),
std::move(callback)));
checksum_address.ToChecksumAddress(), message_str,
std::move(message_bytes), std::move(callback), false));
}

void BraveWalletProviderImpl::SignTypedMessage(
const std::string& address,
const std::string& message,
const std::string& message_to_sign,
base::Value domain,
SignTypedMessageCallback callback) {
std::vector<uint8_t> eip712_hash;
if (!EthAddress::IsValidAddress(address) ||
!base::HexStringToBytes(message_to_sign, &eip712_hash) ||
eip712_hash.size() != 32 || !domain.is_dict()) {
std::move(callback).Run(
"", static_cast<int>(ProviderErrors::kInvalidParams),
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}

auto chain_id = domain.FindDoubleKey("chainId");
if (chain_id) {
const std::string chain_id_hex =
Uint256ValueToHex((uint256_t)(uint64_t)*chain_id);
if (chain_id_hex != rpc_controller_->GetChainId()) {
std::move(callback).Run(
"", static_cast<int>(ProviderErrors::kInternalError),
l10n_util::GetStringFUTF8(
IDS_BRAVE_WALLET_SIGN_TYPED_MESSAGE_CHAIN_ID_MISMATCH,
base::ASCIIToUTF16(chain_id_hex)));
return;
}
}

// Convert to checksum address
auto checksum_address = EthAddress::FromHex(address);
GetAllowedAccounts(base::BindOnce(
&BraveWalletProviderImpl::ContinueSignMessage, weak_factory_.GetWeakPtr(),
checksum_address.ToChecksumAddress(), message, std::move(eip712_hash),
std::move(callback), true));
}

void BraveWalletProviderImpl::ContinueSignMessage(
const std::string& address,
std::vector<uint8_t>&& message,
const std::string& message,
std::vector<uint8_t>&& message_to_sign,
SignMessageCallback callback,
bool is_eip712,
bool success,
const std::vector<std::string>& allowed_accounts) {
if (!CheckAccountAllowed(address, allowed_accounts)) {
Expand All @@ -344,25 +389,21 @@ void BraveWalletProviderImpl::ContinueSignMessage(
return;
}

std::string message_str(message.begin(), message.end());
if (!base::IsStringUTF8(message_str))
message_str = ToHex(message_str);

auto request =
mojom::SignMessageRequest::New(sign_message_id_++, address, message_str);
mojom::SignMessageRequest::New(sign_message_id_++, address, message);
if (keyring_controller_->IsHardwareAccount(address)) {
brave_wallet_service_->AddSignMessageRequest(
std::move(request),
base::BindOnce(
&BraveWalletProviderImpl::OnHardwareSignMessageRequestProcessed,
weak_factory_.GetWeakPtr(), std::move(callback), address,
std::move(message)));
std::move(message_to_sign), is_eip712));
} else {
brave_wallet_service_->AddSignMessageRequest(
std::move(request),
base::BindOnce(&BraveWalletProviderImpl::OnSignMessageRequestProcessed,
weak_factory_.GetWeakPtr(), std::move(callback), address,
std::move(message)));
std::move(message_to_sign), is_eip712));
}
delegate_->ShowBubble();
}
Expand All @@ -371,6 +412,7 @@ void BraveWalletProviderImpl::OnSignMessageRequestProcessed(
SignMessageCallback callback,
const std::string& address,
std::vector<uint8_t>&& message,
bool is_eip712,
bool approved,
const std::string& signature,
const std::string& error) {
Expand All @@ -381,8 +423,8 @@ void BraveWalletProviderImpl::OnSignMessageRequestProcessed(
return;
}

auto signature_with_err =
keyring_controller_->SignMessageByDefaultKeyring(address, message);
auto signature_with_err = keyring_controller_->SignMessageByDefaultKeyring(
address, message, is_eip712);
if (!signature_with_err.signature)
std::move(callback).Run("",
static_cast<int>(ProviderErrors::kInternalError),
Expand All @@ -395,6 +437,7 @@ void BraveWalletProviderImpl::OnHardwareSignMessageRequestProcessed(
SignMessageCallback callback,
const std::string& address,
std::vector<uint8_t>&& message,
bool is_eip712,
bool approved,
const std::string& signature,
const std::string& error) {
Expand Down
11 changes: 10 additions & 1 deletion components/brave_wallet/browser/brave_wallet_provider_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ class BraveWalletProviderImpl final
void SignMessage(const std::string& address,
const std::string& message,
SignMessageCallback callback) override;
void SignTypedMessage(const std::string& address,
const std::string& message,
const std::string& message_to_sign,
base::Value domain,
SignTypedMessageCallback callback) override;
void OnGetAllowedAccounts(GetAllowedAccountsCallback callback,
bool success,
const std::vector<std::string>& accounts);
Expand Down Expand Up @@ -135,8 +140,10 @@ class BraveWalletProviderImpl final
bool success,
const std::vector<std::string>& allowed_accounts);
void ContinueSignMessage(const std::string& address,
std::vector<uint8_t>&& message,
const std::string& message,
std::vector<uint8_t>&& message_to_sign,
SignMessageCallback callback,
bool is_eip712,
bool success,
const std::vector<std::string>& allowed_accounts);
bool CheckAccountAllowed(const std::string& account,
Expand All @@ -161,12 +168,14 @@ class BraveWalletProviderImpl final
void OnSignMessageRequestProcessed(SignMessageCallback callback,
const std::string& address,
std::vector<uint8_t>&& message,
bool is_eip712,
bool approved,
const std::string& signature,
const std::string& error);
void OnHardwareSignMessageRequestProcessed(SignMessageCallback callback,
const std::string& address,
std::vector<uint8_t>&& message,
bool is_eip712,
bool approved,
const std::string& signature,
const std::string& error);
Expand Down
24 changes: 17 additions & 7 deletions components/brave_wallet/browser/hd_keyring.cc
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,27 @@ void HDKeyring::SignTransaction(const std::string& address,

std::vector<uint8_t> HDKeyring::SignMessage(const std::string& address,
const std::vector<uint8_t>& message,
uint256_t chain_id) {
uint256_t chain_id,
bool is_eip712) {
HDKey* hd_key = GetHDKeyFromAddress(address);
if (!hd_key)
return std::vector<uint8_t>();

std::string prefix("\x19");
prefix += std::string("Ethereum Signed Message:\n" +
base::NumberToString(message.size()));
std::vector<uint8_t> hash_input(prefix.begin(), prefix.end());
hash_input.insert(hash_input.end(), message.begin(), message.end());
const std::vector<uint8_t> hash = KeccakHash(hash_input);
std::vector<uint8_t> hash;
if (!is_eip712) {
std::string prefix("\x19");
prefix += std::string("Ethereum Signed Message:\n" +
base::NumberToString(message.size()));
std::vector<uint8_t> hash_input(prefix.begin(), prefix.end());
hash_input.insert(hash_input.end(), message.begin(), message.end());
hash = KeccakHash(hash_input);
} else {
// eip712 hash is Keccak
if (message.size() != 32)
return std::vector<uint8_t>();

hash = message;
}

int recid;
std::vector<uint8_t> signature = hd_key->Sign(hash, &recid);
Expand Down
3 changes: 2 additions & 1 deletion components/brave_wallet/browser/hd_keyring.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ class HDKeyring {
// eth_sign
virtual std::vector<uint8_t> SignMessage(const std::string& address,
const std::vector<uint8_t>& message,
uint256_t chain_id);
uint256_t chain_id,
bool is_eip712);

HDKey* GetHDKeyFromAddress(const std::string& address);

Expand Down
Loading