Skip to content

Commit

Permalink
Merge pull request #329 from keepkey/feature-gnocontract
Browse files Browse the repository at this point in the history
Feature gnocontract
  • Loading branch information
pastaghost committed Dec 21, 2022
2 parents a3c1c15 + 9dad74d commit 32ec2f7
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 3 deletions.
2 changes: 1 addition & 1 deletion deps/python-keepkey
5 changes: 5 additions & 0 deletions include/keepkey/firmware/ethereum_contracts.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* This file is part of the KeepKey project.
*
* Copyright (C) 2022 markrypto
* Copyright (C) 2019 ShapeShift
*
* This library is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -33,4 +34,8 @@ bool ethereum_contractHandled(uint32_t data_total, const EthereumSignTx *msg,
bool ethereum_contractConfirmed(uint32_t data_total, const EthereumSignTx *msg,
const HDNode *node);

bool ethereum_cFuncHandled(const EthereumSignTx *msg);

bool ethereum_cFuncConfirmed(uint32_t data_total, const EthereumSignTx *msg);

#endif
34 changes: 34 additions & 0 deletions include/keepkey/firmware/ethereum_contracts/confuncs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* This file is part of the KeepKey project.
*
* Copyright (C) 2022 markrypto
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef KEEPKEY_FIRMWARE_CONTRACTFUNCS_H
#define KEEPKEY_FIRMWARE_CONTRACTFUNCS_H

#include <inttypes.h>
#include <stdbool.h>

// used by gnosis proxy contracts
#define EXEC_TRANSACTION "\x6a\x76\x12\x02"

typedef struct _EthereumSignTx EthereumSignTx;

bool cf_isExecTx(const EthereumSignTx *msg);
bool cf_confirmExecTx(uint32_t data_total, const EthereumSignTx *msg);

#endif
1 change: 1 addition & 0 deletions lib/firmware/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(sources
ethereum.c
ethereum_contracts.c
ethereum_contracts/makerdao.c
ethereum_contracts/contractfuncs.c
ethereum_contracts/saproxy.c
ethereum_contracts/zxappliquid.c
ethereum_contracts/thortx.c
Expand Down
4 changes: 4 additions & 0 deletions lib/firmware/eip712.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,11 @@ void dsConfirm(void) {

if (NULL != dschainId) {
noChain = false;
#ifdef EMULATOR
sscanf((char *)dschainId, "%d", &chainInt);
#else
sscanf((char *)dschainId, "%ld", &chainInt);
#endif
// As more chains are supported, add icon choice below
// TBD: not implemented for first release
// if (chainInt == 1) {
Expand Down
28 changes: 26 additions & 2 deletions lib/firmware/ethereum.c
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,8 @@ static bool ethereum_signing_check(EthereumSignTx *msg) {

void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node,
bool needs_confirm) {
char confirm_body_message[121] = {0};

ethereum_signing = true;
sha3_256_Init(&keccak_ctx);

Expand Down Expand Up @@ -696,6 +698,30 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node,
data_needs_confirm = false;
}

// contract function may be recognized even though contract is not, e.g., gnosis safe execTransaction
if (msg->to.size && ethereum_cFuncHandled(msg)) {
// confirm contract address
char addr[43] = "0x";
ethereum_address_checksum(msg->to.bytes, addr + 2, false, chain_id);

if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "contract address", "%s", addr)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
"Signing cancelled by user");
ethereum_signing_abort();
return;
}

// confirm contract data
if (!ethereum_cFuncConfirmed(data_total, msg)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
"Signing cancelled by user");
ethereum_signing_abort();
return;
}
needs_confirm = false;
data_needs_confirm = false;
}

// detect ERC-20 token
if (data_total == 68 && ethereum_isStandardERC20Transfer(msg)) {
token = tokenByChainAddress(chain_id, msg->to.bytes);
Expand All @@ -707,9 +733,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node,
is_approve = true;
}

char confirm_body_message[BODY_CHAR_MAX];
if (needs_confirm) {
memset(confirm_body_message, 0, sizeof(confirm_body_message));
if (token != NULL) {
layoutEthereumConfirmTx(
msg->data_initial_chunk.bytes + 16, 20,
Expand Down
15 changes: 15 additions & 0 deletions lib/firmware/ethereum_contracts.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* This file is part of the KeepKey project.
*
* Copyright (C) 2022 markrypto
* Copyright (C) 2019 ShapeShift
*
* This library is free software: you can redistribute it and/or modify
Expand All @@ -19,6 +20,7 @@

#include "keepkey/firmware/ethereum_contracts.h"

#include "keepkey/firmware/ethereum_contracts/confuncs.h"
#include "keepkey/firmware/ethereum_contracts/saproxy.h"
#include "keepkey/firmware/ethereum_contracts/thortx.h"
#include "keepkey/firmware/ethereum_contracts/zxappliquid.h"
Expand All @@ -27,6 +29,19 @@
#include "keepkey/firmware/ethereum_contracts/zxswap.h"
#include "keepkey/firmware/ethereum_contracts/makerdao.h"


bool ethereum_cFuncHandled(const EthereumSignTx *msg) {
if (cf_isExecTx(msg)) return true; // used in gnosis proxy contracts
return false;
}

bool ethereum_cFuncConfirmed(uint32_t data_total, const EthereumSignTx *msg) {
if (cf_isExecTx(msg)) {
return cf_confirmExecTx(data_total, msg);
}
return false;
}

bool ethereum_contractHandled(uint32_t data_total, const EthereumSignTx *msg,
const HDNode *node) {
(void)node;
Expand Down
191 changes: 191 additions & 0 deletions lib/firmware/ethereum_contracts/contractfuncs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* This file is part of the KeepKey project.
*
* Copyright (C) 2022 markrypto
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/

#include "keepkey/firmware/ethereum_contracts/confuncs.h"

#include "keepkey/board/confirm_sm.h"
#include "keepkey/board/util.h"
#include "keepkey/firmware/ethereum.h"
#include "keepkey/firmware/ethereum_tokens.h"
#include "keepkey/firmware/fsm.h"
#include "trezor/crypto/address.h"

bool cf_isExecTx(const EthereumSignTx *msg) {
if (memcmp(msg->data_initial_chunk.bytes, EXEC_TRANSACTION, 4) == 0)
return true;

return false;
}

bool cf_confirmExecTx(uint32_t data_total, const EthereumSignTx *msg) {
extern const ecdsa_curve secp256k1;
(void)data_total;
char confStr[131];
char contractStr[41];
uint8_t *to, *gasToken, *refundReceiver, *data;
bignum256 bnNum, gasPrice;
char txStr[32], safeGasStr[32], baseGasStr[32], gasPriceStr[32];
TokenType const *TokenData;
int8_t *operation;
uint32_t offset, dlen;
char const *confDatStr, *confDatStr2;
unsigned ctr, n, chunk, chunkSize;
const char *title = "contract func exec_tx";

to = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 0*32 + 12);
for (ctr=0; ctr<20; ctr++) {
snprintf(&confStr[ctr*2], 3, "%02x", to[ctr]);
}
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
title, "Sending to %s", confStr)) {
return false;
}

// value
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 1*32, 32, &bnNum);
ethereumFormatAmount(&bnNum, NULL, 1 /*chainId*/, txStr, sizeof(txStr));
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
title, "amount %s", txStr)) {
return false;
}

// get data bytes
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2*32, 32, &bnNum); // data offset
offset = bn_write_uint32(&bnNum);
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + offset, 32, &bnNum); // data len
dlen = bn_write_uint32(&bnNum);
data = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 32 + offset);

n = 1;
chunkSize = 39;
while (true) {
chunk=chunkSize*(n-1);
for (ctr=chunk; ctr<chunkSize+chunk && ctr<dlen; ctr++) {
snprintf(&confStr[(ctr-chunk)*2], 3, "%02x", data[ctr]);
}
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
title, "Data payload %d: %s", n, confStr)) {
return false;
}
if (ctr >= dlen) {
break;
}
n++;
}

// operation is an enum: https://github.com/safe-global/safe-contracts/blob/main/contracts/common/Enum.sol#L7
operation = (int8_t *)(msg->data_initial_chunk.bytes + 4 + 3*32);
{
char *opStr;
switch (*operation) {
case 0:
opStr = "Call";
break;
case 1:
opStr = "DelegateCall";
break;
default:
opStr = "Unknown";
}
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
title, "Operation: %s", opStr)) {
return false;
}
}

bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 6*32, 32, &gasPrice); // used for payment calc
ethereumFormatAmount(&gasPrice, NULL, 1 /*chainId*/, gasPriceStr, sizeof(gasPriceStr));

bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 4*32, 32, &bnNum); // safe transaction gas
bn_multiply(&gasPrice, &bnNum, &secp256k1.prime);
ethereumFormatAmount(&bnNum, NULL, 1 /*chainId*/, safeGasStr, sizeof(safeGasStr));

bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 5*32, 32, &bnNum); // independent gas needed
bn_multiply(&gasPrice, &bnNum, &secp256k1.prime);
ethereumFormatAmount(&bnNum, NULL, 1 /*chainId*/, baseGasStr, sizeof(baseGasStr));

if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
title, "Safe tx gas: %s\nBase gas: %s\nGas price: %s", safeGasStr, baseGasStr, gasPriceStr)) {
return false;
}

// gas token
gasToken = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 7*32 + 12); // token to be used for gas payment
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 7*32, 32, &bnNum); // used to check for zero
if (bn_is_zero(&bnNum)) {
// gas payment in ETH
confDatStr = "ETH";
} else {
// gas payment in token
TokenData = tokenByChainAddress(1 /*chainId*/, (uint8_t *)gasToken);
if (strncmp(TokenData->ticker, " UNKN", 5) == 0) {
for (ctr=0; ctr<20; ctr++) {
snprintf(&contractStr[2*ctr], 3, "%02x", TokenData->address[ctr]);
}
confDatStr = contractStr;
} else {
confDatStr = TokenData->ticker;
}
}

refundReceiver = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 8*32 + 12); // gas refund receiver
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 8*32, 32, &bnNum); // used to check for zero
if (bn_is_zero(&bnNum)) {
// gas refund receiver is origin
confDatStr2 = "tx origin";
} else {
// gas refund receiver address
for (ctr=0; ctr<20; ctr++) {
snprintf(&contractStr[2*ctr], 3, "%02x", refundReceiver[ctr]);
}
confDatStr2 = contractStr;
}


if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
title, "Gas payment token: %s\nGas refund address: %s", confDatStr, confDatStr2)) {
return false;
}

// get signature data
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 9*32, 32, &bnNum); // sig offset
offset = bn_write_uint32(&bnNum);
bn_from_bytes(msg->data_initial_chunk.bytes + 4 + offset, 32, &bnNum); // sig data len
dlen = bn_write_uint32(&bnNum);
data = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 32 + offset);


n = 1;
chunkSize = 65;
while (true) {
chunk=chunkSize*(n-1);
for (ctr=chunk; ctr<chunkSize+chunk && ctr<dlen; ctr++) {
snprintf(&confStr[(ctr-chunk)*2], 3, "%02x", data[ctr]);
}
if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput,
title, "Signature %d: %s", n, confStr)) {
return false;
}
if (ctr >= dlen) {
break;
}
n++;
}
return true;
}

0 comments on commit 32ec2f7

Please sign in to comment.