Skip to content

Commit 32ec2f7

Browse files
authored
Merge pull request #329 from keepkey/feature-gnocontract
Feature gnocontract
2 parents a3c1c15 + 9dad74d commit 32ec2f7

File tree

8 files changed

+277
-3
lines changed

8 files changed

+277
-3
lines changed

include/keepkey/firmware/ethereum_contracts.h

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* This file is part of the KeepKey project.
33
*
4+
* Copyright (C) 2022 markrypto
45
* Copyright (C) 2019 ShapeShift
56
*
67
* This library is free software: you can redistribute it and/or modify
@@ -33,4 +34,8 @@ bool ethereum_contractHandled(uint32_t data_total, const EthereumSignTx *msg,
3334
bool ethereum_contractConfirmed(uint32_t data_total, const EthereumSignTx *msg,
3435
const HDNode *node);
3536

37+
bool ethereum_cFuncHandled(const EthereumSignTx *msg);
38+
39+
bool ethereum_cFuncConfirmed(uint32_t data_total, const EthereumSignTx *msg);
40+
3641
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* This file is part of the KeepKey project.
3+
*
4+
* Copyright (C) 2022 markrypto
5+
*
6+
* This library is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Lesser General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This library is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this library. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#ifndef KEEPKEY_FIRMWARE_CONTRACTFUNCS_H
21+
#define KEEPKEY_FIRMWARE_CONTRACTFUNCS_H
22+
23+
#include <inttypes.h>
24+
#include <stdbool.h>
25+
26+
// used by gnosis proxy contracts
27+
#define EXEC_TRANSACTION "\x6a\x76\x12\x02"
28+
29+
typedef struct _EthereumSignTx EthereumSignTx;
30+
31+
bool cf_isExecTx(const EthereumSignTx *msg);
32+
bool cf_confirmExecTx(uint32_t data_total, const EthereumSignTx *msg);
33+
34+
#endif

lib/firmware/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ set(sources
1111
ethereum.c
1212
ethereum_contracts.c
1313
ethereum_contracts/makerdao.c
14+
ethereum_contracts/contractfuncs.c
1415
ethereum_contracts/saproxy.c
1516
ethereum_contracts/zxappliquid.c
1617
ethereum_contracts/thortx.c

lib/firmware/eip712.c

+4
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,11 @@ void dsConfirm(void) {
370370

371371
if (NULL != dschainId) {
372372
noChain = false;
373+
#ifdef EMULATOR
374+
sscanf((char *)dschainId, "%d", &chainInt);
375+
#else
373376
sscanf((char *)dschainId, "%ld", &chainInt);
377+
#endif
374378
// As more chains are supported, add icon choice below
375379
// TBD: not implemented for first release
376380
// if (chainInt == 1) {

lib/firmware/ethereum.c

+26-2
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,8 @@ static bool ethereum_signing_check(EthereumSignTx *msg) {
597597

598598
void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node,
599599
bool needs_confirm) {
600+
char confirm_body_message[121] = {0};
601+
600602
ethereum_signing = true;
601603
sha3_256_Init(&keccak_ctx);
602604

@@ -696,6 +698,30 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node,
696698
data_needs_confirm = false;
697699
}
698700

701+
// contract function may be recognized even though contract is not, e.g., gnosis safe execTransaction
702+
if (msg->to.size && ethereum_cFuncHandled(msg)) {
703+
// confirm contract address
704+
char addr[43] = "0x";
705+
ethereum_address_checksum(msg->to.bytes, addr + 2, false, chain_id);
706+
707+
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "contract address", "%s", addr)) {
708+
fsm_sendFailure(FailureType_Failure_ActionCancelled,
709+
"Signing cancelled by user");
710+
ethereum_signing_abort();
711+
return;
712+
}
713+
714+
// confirm contract data
715+
if (!ethereum_cFuncConfirmed(data_total, msg)) {
716+
fsm_sendFailure(FailureType_Failure_ActionCancelled,
717+
"Signing cancelled by user");
718+
ethereum_signing_abort();
719+
return;
720+
}
721+
needs_confirm = false;
722+
data_needs_confirm = false;
723+
}
724+
699725
// detect ERC-20 token
700726
if (data_total == 68 && ethereum_isStandardERC20Transfer(msg)) {
701727
token = tokenByChainAddress(chain_id, msg->to.bytes);
@@ -707,9 +733,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node,
707733
is_approve = true;
708734
}
709735

710-
char confirm_body_message[BODY_CHAR_MAX];
711736
if (needs_confirm) {
712-
memset(confirm_body_message, 0, sizeof(confirm_body_message));
713737
if (token != NULL) {
714738
layoutEthereumConfirmTx(
715739
msg->data_initial_chunk.bytes + 16, 20,

lib/firmware/ethereum_contracts.c

+15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* This file is part of the KeepKey project.
33
*
4+
* Copyright (C) 2022 markrypto
45
* Copyright (C) 2019 ShapeShift
56
*
67
* This library is free software: you can redistribute it and/or modify
@@ -19,6 +20,7 @@
1920

2021
#include "keepkey/firmware/ethereum_contracts.h"
2122

23+
#include "keepkey/firmware/ethereum_contracts/confuncs.h"
2224
#include "keepkey/firmware/ethereum_contracts/saproxy.h"
2325
#include "keepkey/firmware/ethereum_contracts/thortx.h"
2426
#include "keepkey/firmware/ethereum_contracts/zxappliquid.h"
@@ -27,6 +29,19 @@
2729
#include "keepkey/firmware/ethereum_contracts/zxswap.h"
2830
#include "keepkey/firmware/ethereum_contracts/makerdao.h"
2931

32+
33+
bool ethereum_cFuncHandled(const EthereumSignTx *msg) {
34+
if (cf_isExecTx(msg)) return true; // used in gnosis proxy contracts
35+
return false;
36+
}
37+
38+
bool ethereum_cFuncConfirmed(uint32_t data_total, const EthereumSignTx *msg) {
39+
if (cf_isExecTx(msg)) {
40+
return cf_confirmExecTx(data_total, msg);
41+
}
42+
return false;
43+
}
44+
3045
bool ethereum_contractHandled(uint32_t data_total, const EthereumSignTx *msg,
3146
const HDNode *node) {
3247
(void)node;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
* This file is part of the KeepKey project.
3+
*
4+
* Copyright (C) 2022 markrypto
5+
*
6+
* This library is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Lesser General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This library is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this library. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "keepkey/firmware/ethereum_contracts/confuncs.h"
21+
22+
#include "keepkey/board/confirm_sm.h"
23+
#include "keepkey/board/util.h"
24+
#include "keepkey/firmware/ethereum.h"
25+
#include "keepkey/firmware/ethereum_tokens.h"
26+
#include "keepkey/firmware/fsm.h"
27+
#include "trezor/crypto/address.h"
28+
29+
bool cf_isExecTx(const EthereumSignTx *msg) {
30+
if (memcmp(msg->data_initial_chunk.bytes, EXEC_TRANSACTION, 4) == 0)
31+
return true;
32+
33+
return false;
34+
}
35+
36+
bool cf_confirmExecTx(uint32_t data_total, const EthereumSignTx *msg) {
37+
extern const ecdsa_curve secp256k1;
38+
(void)data_total;
39+
char confStr[131];
40+
char contractStr[41];
41+
uint8_t *to, *gasToken, *refundReceiver, *data;
42+
bignum256 bnNum, gasPrice;
43+
char txStr[32], safeGasStr[32], baseGasStr[32], gasPriceStr[32];
44+
TokenType const *TokenData;
45+
int8_t *operation;
46+
uint32_t offset, dlen;
47+
char const *confDatStr, *confDatStr2;
48+
unsigned ctr, n, chunk, chunkSize;
49+
const char *title = "contract func exec_tx";
50+
51+
to = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 0*32 + 12);
52+
for (ctr=0; ctr<20; ctr++) {
53+
snprintf(&confStr[ctr*2], 3, "%02x", to[ctr]);
54+
}
55+
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
56+
title, "Sending to %s", confStr)) {
57+
return false;
58+
}
59+
60+
// value
61+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 1*32, 32, &bnNum);
62+
ethereumFormatAmount(&bnNum, NULL, 1 /*chainId*/, txStr, sizeof(txStr));
63+
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
64+
title, "amount %s", txStr)) {
65+
return false;
66+
}
67+
68+
// get data bytes
69+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2*32, 32, &bnNum); // data offset
70+
offset = bn_write_uint32(&bnNum);
71+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + offset, 32, &bnNum); // data len
72+
dlen = bn_write_uint32(&bnNum);
73+
data = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 32 + offset);
74+
75+
n = 1;
76+
chunkSize = 39;
77+
while (true) {
78+
chunk=chunkSize*(n-1);
79+
for (ctr=chunk; ctr<chunkSize+chunk && ctr<dlen; ctr++) {
80+
snprintf(&confStr[(ctr-chunk)*2], 3, "%02x", data[ctr]);
81+
}
82+
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
83+
title, "Data payload %d: %s", n, confStr)) {
84+
return false;
85+
}
86+
if (ctr >= dlen) {
87+
break;
88+
}
89+
n++;
90+
}
91+
92+
// operation is an enum: https://github.com/safe-global/safe-contracts/blob/main/contracts/common/Enum.sol#L7
93+
operation = (int8_t *)(msg->data_initial_chunk.bytes + 4 + 3*32);
94+
{
95+
char *opStr;
96+
switch (*operation) {
97+
case 0:
98+
opStr = "Call";
99+
break;
100+
case 1:
101+
opStr = "DelegateCall";
102+
break;
103+
default:
104+
opStr = "Unknown";
105+
}
106+
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
107+
title, "Operation: %s", opStr)) {
108+
return false;
109+
}
110+
}
111+
112+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 6*32, 32, &gasPrice); // used for payment calc
113+
ethereumFormatAmount(&gasPrice, NULL, 1 /*chainId*/, gasPriceStr, sizeof(gasPriceStr));
114+
115+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 4*32, 32, &bnNum); // safe transaction gas
116+
bn_multiply(&gasPrice, &bnNum, &secp256k1.prime);
117+
ethereumFormatAmount(&bnNum, NULL, 1 /*chainId*/, safeGasStr, sizeof(safeGasStr));
118+
119+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 5*32, 32, &bnNum); // independent gas needed
120+
bn_multiply(&gasPrice, &bnNum, &secp256k1.prime);
121+
ethereumFormatAmount(&bnNum, NULL, 1 /*chainId*/, baseGasStr, sizeof(baseGasStr));
122+
123+
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
124+
title, "Safe tx gas: %s\nBase gas: %s\nGas price: %s", safeGasStr, baseGasStr, gasPriceStr)) {
125+
return false;
126+
}
127+
128+
// gas token
129+
gasToken = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 7*32 + 12); // token to be used for gas payment
130+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 7*32, 32, &bnNum); // used to check for zero
131+
if (bn_is_zero(&bnNum)) {
132+
// gas payment in ETH
133+
confDatStr = "ETH";
134+
} else {
135+
// gas payment in token
136+
TokenData = tokenByChainAddress(1 /*chainId*/, (uint8_t *)gasToken);
137+
if (strncmp(TokenData->ticker, " UNKN", 5) == 0) {
138+
for (ctr=0; ctr<20; ctr++) {
139+
snprintf(&contractStr[2*ctr], 3, "%02x", TokenData->address[ctr]);
140+
}
141+
confDatStr = contractStr;
142+
} else {
143+
confDatStr = TokenData->ticker;
144+
}
145+
}
146+
147+
refundReceiver = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 8*32 + 12); // gas refund receiver
148+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 8*32, 32, &bnNum); // used to check for zero
149+
if (bn_is_zero(&bnNum)) {
150+
// gas refund receiver is origin
151+
confDatStr2 = "tx origin";
152+
} else {
153+
// gas refund receiver address
154+
for (ctr=0; ctr<20; ctr++) {
155+
snprintf(&contractStr[2*ctr], 3, "%02x", refundReceiver[ctr]);
156+
}
157+
confDatStr2 = contractStr;
158+
}
159+
160+
161+
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
162+
title, "Gas payment token: %s\nGas refund address: %s", confDatStr, confDatStr2)) {
163+
return false;
164+
}
165+
166+
// get signature data
167+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 9*32, 32, &bnNum); // sig offset
168+
offset = bn_write_uint32(&bnNum);
169+
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + offset, 32, &bnNum); // sig data len
170+
dlen = bn_write_uint32(&bnNum);
171+
data = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 32 + offset);
172+
173+
174+
n = 1;
175+
chunkSize = 65;
176+
while (true) {
177+
chunk=chunkSize*(n-1);
178+
for (ctr=chunk; ctr<chunkSize+chunk && ctr<dlen; ctr++) {
179+
snprintf(&confStr[(ctr-chunk)*2], 3, "%02x", data[ctr]);
180+
}
181+
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
182+
title, "Signature %d: %s", n, confStr)) {
183+
return false;
184+
}
185+
if (ctr >= dlen) {
186+
break;
187+
}
188+
n++;
189+
}
190+
return true;
191+
}

0 commit comments

Comments
 (0)