Skip to content

Commit 8c3330b

Browse files
committed
feat(legacy): add support for EIP-712 signing
1 parent 0dca5a5 commit 8c3330b

20 files changed

+1385
-35
lines changed

common/protob/messages-management.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ message Features {
112112
Capability_ShamirGroups = 16 [(bitcoin_only) = true];
113113
Capability_PassphraseEntry = 17 [(bitcoin_only) = true]; // the device is capable of passphrase entry directly on the device
114114
Capability_AttachToPin = 8000;
115+
Capability_EthereumTypedData = 1000; // the capability of handling EIP-712 typed data
115116
}
116117
optional BackupType backup_type = 31; // type of device backup (BIP-39 / SLIP-39 basic / SLIP-39 advanced)
117118
optional bool sd_card_present = 32; // is SD card present

common/protob/messages.proto

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,12 @@ enum MessageType {
201201
MessageType_EthereumSignMessage = 64 [(wire_in) = true];
202202
MessageType_EthereumVerifyMessage = 65 [(wire_in) = true];
203203
MessageType_EthereumMessageSignature = 66 [(wire_out) = true];
204-
MessageType_EthereumSignTypedData = 464 [(wire_in) = true];
205-
MessageType_EthereumTypedDataStructRequest = 465 [(wire_out) = true];
206-
MessageType_EthereumTypedDataStructAck = 466 [(wire_in) = true];
207-
MessageType_EthereumTypedDataValueRequest = 467 [(wire_out) = true];
208-
MessageType_EthereumTypedDataValueAck = 468 [(wire_in) = true];
204+
// MessageType_EthereumSignTypedData = 464 [(wire_in) = true];
205+
// MessageType_EthereumTypedDataStructRequest = 465 [(wire_out) = true];
206+
// MessageType_EthereumTypedDataStructAck = 466 [(wire_in) = true];
207+
// MessageType_EthereumTypedDataValueRequest = 467 [(wire_out) = true];
208+
// MessageType_EthereumTypedDataValueAck = 468 [(wire_in) = true];
209+
reserved 464 to 468;
209210
MessageType_EthereumTypedDataSignature = 469 [(wire_out) = true];
210211
MessageType_EthereumSignTypedHash = 470 [(wire_in) = true];
211212

@@ -223,9 +224,9 @@ enum MessageType {
223224
MessageType_EthereumMessageSignatureOneKey = 20110 [(wire_out) = true];
224225
MessageType_EthereumSignTypedDataOneKey = 20111 [(wire_in) = true];
225226
MessageType_EthereumTypedDataStructRequestOneKey = 20112 [(wire_out) = true];
226-
MessageType_EthereumTypedDataStructAckOneKey = 20113 [(wire_in) = true];
227+
MessageType_EthereumTypedDataStructAckOneKey = 20113 [(wire_in) = true, (wire_no_fsm) = true];
227228
MessageType_EthereumTypedDataValueRequestOneKey = 20114 [(wire_out) = true];
228-
MessageType_EthereumTypedDataValueAckOneKey = 20115 [(wire_in) = true];
229+
MessageType_EthereumTypedDataValueAckOneKey = 20115 [(wire_in) = true, (wire_no_fsm) = true];
229230
MessageType_EthereumTypedDataSignatureOneKey = 20116 [(wire_out) = true];
230231
MessageType_EthereumSignTypedHashOneKey = 20117 [(wire_in) = true];
231232
reserved 20118, 20119;

legacy/firmware/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ OBJS += ../vendor/trezor-crypto/cash_addr.o
256256
OBJS += protob/messages-ethereum.pb.o
257257
OBJS += protob/messages-ethereum-definitions.pb.o
258258
OBJS += protob/messages-ethereum-onekey.pb.o
259+
OBJS += protob/messages-ethereum-eip712-onekey.pb.o
259260
OBJS += protob/messages-nem.pb.o
260261
OBJS += protob/messages-solana.pb.o
261262
OBJS += protob/messages-starcoin.pb.o
@@ -302,7 +303,7 @@ else
302303
#CFLAGS += -fstack-protector-strong
303304
endif
304305
CFLAGS += -Wno-sequence-point
305-
CFLAGS += -I../vendor/nanopb -Iprotob -DPB_FIELD_16BIT=1 -DPB_ENCODE_ARRAYS_UNPACKED=1 -DPB_VALIDATE_UTF8=1
306+
CFLAGS += -I../vendor/nanopb -Iprotob -DPB_FIELD_16BIT=1 -DPB_ENABLE_MALLOC=1 -DPB_ENCODE_ARRAYS_UNPACKED=1 -DPB_VALIDATE_UTF8=1
306307
CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"'
307308
CFLAGS += -DBUILD_ID='"$(NAME)"'
308309
CFLAGS += -DUSE_MONERO=0

legacy/firmware/ethereum_onekey.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "ethereum_networks_onekey.h"
2929
#include "ethereum_onekey.h"
3030
#include "ethereum_tokens_onekey.h"
31+
#include "ethereum_typed_data.h"
3132
#include "fsm.h"
3233
#include "gettext.h"
3334
#include "layout2.h"
@@ -1650,6 +1651,141 @@ void ethereum_typed_hash_sign_onekey(const EthereumSignTypedHashOneKey *msg,
16501651
msg_write(MessageType_MessageType_EthereumTypedDataSignatureOneKey, resp);
16511652
}
16521653

1654+
static bool is_string_in_list(const char *str, const char *const *list,
1655+
size_t list_size) {
1656+
for (size_t i = 0; i < list_size; i++) {
1657+
if (strcmp(str, list[i]) == 0) {
1658+
return true;
1659+
}
1660+
}
1661+
return false;
1662+
}
1663+
static bool typed_data_confirm_final(void) {
1664+
oledClear();
1665+
layoutHeader(_(T_CONFIRM_TYPED_DATA));
1666+
char confirm_text[128] = {0};
1667+
snprintf(confirm_text, 128, "%s",
1668+
_(C__DO_YOU_WANT_TO_SIGN_THIS_CHAIN_STR_MESSAGE_QUES));
1669+
bracket_replace(confirm_text, "EIP712");
1670+
oledDrawStringAdapter(0, 13, confirm_text, FONT_STANDARD);
1671+
layoutButtonNoAdapter(NULL, &bmp_bottom_left_close);
1672+
layoutButtonYesAdapter(NULL, &bmp_bottom_right_arrow);
1673+
oledRefresh();
1674+
return protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false);
1675+
}
1676+
void ethereum_typed_data_sign_onekey(const EthereumSignTypedDataOneKey *msg,
1677+
const HDNode *node,
1678+
EthereumTypedDataSignatureOneKey *resp) {
1679+
TypedDataEnvelope envelope = {0};
1680+
TypedDataEnvelope_init(&envelope, msg->primary_type,
1681+
strlen(msg->primary_type), msg->metamask_v4_compat);
1682+
if (!collect_types(&envelope)) {
1683+
return;
1684+
}
1685+
bool is_permit =
1686+
is_string_in_list(envelope.primary_type, HIGH_RISK_PRIMARY_TYPES_PERMIT,
1687+
sizeof(HIGH_RISK_PRIMARY_TYPES_PERMIT) /
1688+
sizeof(HIGH_RISK_PRIMARY_TYPES_PERMIT[0]));
1689+
bool is_order =
1690+
is_string_in_list(envelope.primary_type, HIGH_RISK_PRIMARY_TYPES_ORDER,
1691+
sizeof(HIGH_RISK_PRIMARY_TYPES_ORDER) /
1692+
sizeof(HIGH_RISK_PRIMARY_TYPES_ORDER[0]));
1693+
char warning_text[128] = {0};
1694+
snprintf(warning_text, 128, "%s", _(I_TYPED_DATA_AUTHORIZATION_WARNING));
1695+
char *warning_type = NULL;
1696+
if (is_permit) {
1697+
warning_type = "Permit";
1698+
} else if (is_order) {
1699+
warning_type = "Order";
1700+
} else {
1701+
warning_type = "signTypedData";
1702+
}
1703+
bracket_replace(warning_text, warning_type);
1704+
// show warning
1705+
layoutDialogCenterAdapterV2(NULL, &bmp_icon_warning, &bmp_bottom_left_close,
1706+
&bmp_bottom_right_arrow, NULL, NULL, NULL, NULL,
1707+
NULL, NULL, warning_text);
1708+
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
1709+
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
1710+
return;
1711+
}
1712+
uint32_t member_path[] = {0};
1713+
uint8_t member_path_len = 1;
1714+
char parent_objects[1][64] = {TYPE_NAME_DOMAIN};
1715+
uint8_t parent_objects_len = 1;
1716+
uint8_t domain_separator[32] = {0};
1717+
display_info_init(&display_info, 16);
1718+
1719+
if (!hash_struct(&envelope, TYPE_NAME_DOMAIN, strlen(TYPE_NAME_DOMAIN),
1720+
member_path, member_path_len, 0, parent_objects,
1721+
parent_objects_len, domain_separator)) {
1722+
display_info_cleanup(&display_info);
1723+
return;
1724+
}
1725+
if (!layoutTypedData(&display_info, TYPE_NAME_DOMAIN)) {
1726+
display_info_cleanup(&display_info);
1727+
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
1728+
return;
1729+
}
1730+
display_info_cleanup(&display_info);
1731+
bool has_message_hash = true;
1732+
if (strncmp(envelope.primary_type, TYPE_NAME_DOMAIN,
1733+
strlen(TYPE_NAME_DOMAIN)) == 0) {
1734+
has_message_hash = false;
1735+
}
1736+
uint8_t message_hash[32] = {0};
1737+
1738+
if (has_message_hash) {
1739+
member_path[0] = 1;
1740+
memzero(parent_objects, sizeof(parent_objects));
1741+
strncpy(parent_objects[0], envelope.primary_type,
1742+
strlen(envelope.primary_type));
1743+
display_info_init(&display_info, 16);
1744+
if (!hash_struct(&envelope, envelope.primary_type,
1745+
strlen(envelope.primary_type), member_path,
1746+
member_path_len, 0, parent_objects, parent_objects_len,
1747+
message_hash)) {
1748+
display_info_cleanup(&display_info);
1749+
return;
1750+
}
1751+
if (!layoutTypedData(&display_info, envelope.primary_type)) {
1752+
display_info_cleanup(&display_info);
1753+
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
1754+
return;
1755+
}
1756+
display_info_cleanup(&display_info);
1757+
}
1758+
1759+
// confirm final
1760+
if (!typed_data_confirm_final()) {
1761+
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
1762+
return;
1763+
}
1764+
uint8_t hash[32] = {0};
1765+
SHA3_CTX ctx = {0};
1766+
sha3_256_Init(&ctx);
1767+
sha3_Update(&ctx, (const uint8_t *)"\x19\x01", 2);
1768+
sha3_Update(&ctx, domain_separator, 32);
1769+
if (has_message_hash) {
1770+
sha3_Update(&ctx, message_hash, 32);
1771+
}
1772+
keccak_Final(&ctx, hash);
1773+
uint8_t v = 0;
1774+
#if EMULATOR
1775+
if (ecdsa_sign_digest(&secp256k1, node->private_key, hash,
1776+
resp->signature.bytes, &v, ethereum_is_canonic) != 0) {
1777+
#else
1778+
if (hdnode_sign_digest(node, hash, resp->signature.bytes, &v,
1779+
ethereum_is_canonic) != 0) {
1780+
#endif
1781+
fsm_sendFailure(FailureType_Failure_ProcessError, "Signing failed");
1782+
return;
1783+
}
1784+
resp->signature.bytes[64] = 27 + v;
1785+
resp->signature.size = 65;
1786+
msg_write(MessageType_MessageType_EthereumTypedDataSignatureOneKey, resp);
1787+
}
1788+
16531789
bool ethereum_parse_onekey(const char *address, uint8_t pubkeyhash[20]) {
16541790
memzero(pubkeyhash, 20);
16551791
size_t len = strlen(address);

legacy/firmware/ethereum_onekey.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <stdbool.h>
2424
#include <stdint.h>
2525
#include "bip32.h"
26+
#include "messages-ethereum-eip712-onekey.pb.h"
2627
#include "messages-ethereum-onekey.pb.h"
2728
#include "messages-ethereum.pb.h"
2829

@@ -44,6 +45,10 @@ int ethereum_message_verify_onekey(const EthereumVerifyMessageOneKey *msg);
4445
void ethereum_typed_hash_sign_onekey(const EthereumSignTypedHashOneKey *msg,
4546
const HDNode *node,
4647
EthereumTypedDataSignatureOneKey *resp);
48+
void ethereum_typed_data_sign_onekey(const EthereumSignTypedDataOneKey *msg,
49+
const HDNode *node,
50+
EthereumTypedDataSignatureOneKey *resp);
51+
4752
bool ethereum_parse_onekey(const char *address, uint8_t pubkeyhash[20]);
4853

4954
// To reduce space, only some EVM networks are supported

0 commit comments

Comments
 (0)