keycard-pro/app/core/core_eth.c

487 lines
14 KiB
C

#include "core.h"
#include "crypto/address.h"
#include "crypto/sha3.h"
#include "crypto/util.h"
#include "keycard/keycard_cmdset.h"
#include "mem.h"
#include "ur/ur_encode.h"
#define ETH_MSG_MAGIC_LEN 26
#define ETH_EIP712_MAGIC_LEN 2
const uint8_t *const ETH_MSG_MAGIC = (uint8_t *) "\031Ethereum Signed Message:\n";
const uint8_t ETH_EIP712_MAGIC[] = { 0x19, 0x01 };
static inline app_err_t core_eth_init_sign(uint32_t* fingerprint) {
keccak_256_Init(&g_core.hash_ctx);
if (core_export_public(g_core.data.key.pub, g_core.data.key.chain, fingerprint, NULL) != ERR_OK) {
return ERR_HW;
}
ethereum_address(g_core.data.key.pub, g_core.address);
return ERR_OK;
}
static inline uint32_t core_eth_get_tx_v_base() {
uint32_t v_base;
if (g_core.data.eth_tx.ctx.txType == EIP1559 || g_core.data.eth_tx.ctx.txType == EIP2930) {
v_base = 0;
} else {
if (g_core.data.eth_tx.content.v == V_NONE) {
v_base = 27;
} else {
v_base = (g_core.data.eth_tx.content.v * 2) + 35;
}
}
return v_base;
}
static inline void core_eth_set_is_message() {
g_core.data.eth_tx.ctx.txType = LEGACY;
g_core.data.eth_tx.content.v = V_NONE;
}
static app_err_t core_eth_sign(keycard_t* kc, uint8_t* out) {
uint8_t digest[SHA3_256_DIGEST_LENGTH];
keccak_Final(&g_core.hash_ctx, digest);
if ((keycard_cmd_sign(kc, g_core.bip44_path, g_core.bip44_path_len, digest, 1) != ERR_OK) || (APDU_SW(&kc->apdu) != 0x9000)) {
return ERR_CRYPTO;
}
uint8_t* data = APDU_RESP(&kc->apdu);
if (keycard_read_signature(data, digest, out) != ERR_OK) {
return ERR_DATA;
}
return ERR_OK;
}
static inline app_err_t core_eth_wait_tx_confirmation() {
return ui_display_eth_tx(g_core.address, &g_core.data.eth_tx.content) == CORE_EVT_UI_OK ? ERR_OK : ERR_CANCEL;
}
static inline app_err_t core_eth_wait_msg_confirmation(const uint8_t* msg, size_t msg_len) {
return ui_display_msg(ADDR_ETH, g_core.address, msg, msg_len) == CORE_EVT_UI_OK ? ERR_OK : ERR_CANCEL;
}
static app_err_t core_eth_process_tx(const uint8_t* data, uint32_t len, uint8_t first_segment) {
if (first_segment) {
// EIP 2718: TransactionType might be present before the TransactionPayload.
uint8_t txType = data[0];
initTx(&g_core.data.eth_tx.ctx, &g_core.hash_ctx, &g_core.data.eth_tx.content);
if (txType >= MIN_TX_TYPE && txType <= MAX_TX_TYPE) {
// Enumerate through all supported txTypes here...
if (txType == EIP2930 || txType == EIP1559) {
keccak_Update(&g_core.hash_ctx, data, 1);
g_core.data.eth_tx.ctx.txType = txType;
data++;
len--;
} else {
return ERR_UNSUPPORTED;
}
} else {
g_core.data.eth_tx.ctx.txType = LEGACY;
}
}
if (g_core.data.eth_tx.ctx.currentField == RLP_NONE) {
return ERR_DATA;
}
parserStatus_e res = processTx(&g_core.data.eth_tx.ctx, data, len);
switch (res) {
case USTREAM_FINISHED:
return core_eth_wait_tx_confirmation();
case USTREAM_PROCESSING:
return ERR_NEED_MORE_DATA;
case USTREAM_FAULT:
default:
return ERR_DATA;
}
}
static app_err_t core_eth_process_msg(const uint8_t* data, uint32_t len, uint8_t first_segment) {
if (first_segment) {
core_eth_set_is_message();
g_core.data.msg.received = 0;
keccak_Update(&g_core.hash_ctx, ETH_MSG_MAGIC, ETH_MSG_MAGIC_LEN);
uint8_t tmp[11];
uint8_t* ascii_len = u32toa(g_core.data.msg.len, tmp, 11);
keccak_Update(&g_core.hash_ctx, ascii_len, 10 - (size_t)(ascii_len - tmp));
}
if ((g_core.data.msg.received + len) > g_core.data.msg.len) {
return ERR_DATA;
}
keccak_Update(&g_core.hash_ctx, data, len);
g_core.data.msg.received += len;
if (g_core.data.msg.received == g_core.data.msg.len) {
return core_eth_wait_msg_confirmation(g_core.data.msg.content, g_core.data.msg.len);
} else {
return ERR_NEED_MORE_DATA;
}
}
static app_err_t core_eth_process_eip712(const uint8_t* data, uint32_t len) {
core_eth_set_is_message();
uint8_t* heap = (uint8_t*) &data[len];
size_t heap_size = MEM_HEAP_SIZE - ((size_t) (heap - g_mem_heap));
app_err_t err;
keccak_Update(&g_core.hash_ctx, ETH_EIP712_MAGIC, ETH_EIP712_MAGIC_LEN);
memset(&g_core.data.eip712, 0, sizeof(eip712_ctx_t));
err = eip712_hash(&g_core.data.eip712, &g_core.hash_ctx, heap, heap_size, (const char*) data, len);
if (err != ERR_OK) {
return err;
}
return ui_display_eip712(g_core.address, &g_core.data.eip712) == CORE_EVT_UI_OK ? ERR_OK : ERR_CANCEL;
}
app_err_t core_eth_usb_get_address(keycard_t* kc, apdu_t* cmd) {
uint8_t* data = APDU_DATA(cmd);
uint16_t len = data[0] * 4;
if (len > BIP44_MAX_PATH_LEN) {
core_usb_err_sw(cmd, 0x6a, 0x80);
return ERR_DATA;
}
uint8_t extended = APDU_P2(cmd) == 1;
SC_BUF(path, BIP44_MAX_PATH_LEN);
memcpy(path, &data[1], len);
uint8_t* out = APDU_RESP(cmd);
app_err_t err = core_export_key(kc, path, len, &out[1], (extended ? &out[107] : NULL));
switch (err) {
case ERR_OK:
break;
case ERR_CRYPTO:
core_usb_err_sw(cmd, 0x69, 0x82);
return ERR_DATA;
default:
core_usb_err_sw(cmd, 0x6f, 0x00);
return ERR_DATA;
}
out[0] = 65;
out[66] = 40;
ethereum_address(&out[1], path);
ethereum_address_checksum(path, (char *)&out[67]);
if (APDU_P1(cmd) == 1) {
if (ui_confirm_eth_address((char *)&out[67]) != CORE_EVT_UI_OK) {
core_usb_err_sw(cmd, 0x69, 0x82);
return ERR_CANCEL;
}
}
if (extended) {
len = 139;
} else {
len = 107;
}
out[len++] = 0x90;
out[len++] = 0x00;
cmd->lr = len;
return ERR_OK;
}
static app_err_t core_eth_usb_init_sign(uint8_t* data) {
g_core.bip44_path_len = data[0] * 4;
if (g_core.bip44_path_len > BIP44_MAX_PATH_LEN) {
g_core.bip44_path_len = 0;
return ERR_DATA;
}
memcpy(g_core.bip44_path, &data[1], g_core.bip44_path_len);
return core_eth_init_sign(NULL);
}
static void core_eth_usb_sign(keycard_t* kc, apdu_t* cmd) {
uint8_t* out = APDU_RESP(cmd);
switch (core_eth_sign(kc, &out[1])) {
case ERR_OK:
out[0] = core_eth_get_tx_v_base() + out[65];
out[65] = 0x90;
out[66] = 0x00;
cmd->lr = 67;
break;
case ERR_CRYPTO:
core_usb_err_sw(cmd, 0x69, 0x82);
break;
default:
core_usb_err_sw(cmd, 0x6f, 0x00);
break;
}
}
app_err_t core_eth_usb_sign_tx(keycard_t* kc, apdu_t* cmd) {
cmd->has_lc = 1;
uint8_t* data = APDU_DATA(cmd);
uint32_t len = APDU_LC(cmd);
uint8_t first = APDU_P1(cmd) == 0;
if (first) {
if (core_eth_usb_init_sign(data) != ERR_OK) {
core_usb_err_sw(cmd, 0x6a, 0x80);
return ERR_DATA;
}
data = &data[1+g_core.bip44_path_len];
len -= g_core.bip44_path_len + 1;
if (len < 1) {
core_usb_err_sw(cmd, 0x6a, 0x80);
return ERR_DATA;
}
g_core.data.eth_tx.content.data = g_mem_heap;
}
app_err_t err = core_eth_process_tx(data, len, first);
switch(err) {
case ERR_OK:
core_eth_usb_sign(kc, cmd);
break;
case ERR_NEED_MORE_DATA:
core_usb_err_sw(cmd, 0x90, 0x00);
break;
case ERR_DATA:
core_usb_err_sw(cmd, 0x6a, 0x80);
break;
case ERR_CANCEL:
core_usb_err_sw(cmd, 0x69, 0x82);
break;
case ERR_UNSUPPORTED:
core_usb_err_sw(cmd, 0x65, 0x01);
break;
default:
core_usb_err_sw(cmd, 0x6f, 0x00);
break;
}
return err;
}
static void core_eth_usb_message_reassemble(keycard_t* kc, apdu_t* cmd, uint8_t** segment, uint32_t* len, uint8_t* first_segment) {
cmd->has_lc = 1;
uint8_t* data = APDU_DATA(cmd);
*len = APDU_LC(cmd);
*first_segment = APDU_P1(cmd) == 0;
if (*first_segment) {
if (core_eth_usb_init_sign(data) != ERR_OK) {
core_usb_err_sw(cmd, 0x6a, 0x80);
return;
}
g_core.data.msg.len = (data[1+g_core.bip44_path_len] << 24) | (data[2+g_core.bip44_path_len] << 16) | (data[3+g_core.bip44_path_len] << 8) | data[4+g_core.bip44_path_len];
if (g_core.data.msg.len > MEM_HEAP_SIZE) {
core_usb_err_sw(cmd, 0x6a, 0x80);
return;
}
g_core.data.msg.received = 0;
*len -= g_core.bip44_path_len + 5;
data = &data[g_core.bip44_path_len + 5];
}
if ((g_core.data.msg.received + *len) > MEM_HEAP_SIZE) {
core_usb_err_sw(cmd, 0x6a, 0x80);
return;
}
g_core.data.msg.content = g_mem_heap;
*segment = &g_core.data.msg.content[g_core.data.msg.received];
memcpy(*segment, data, *len);
}
app_err_t core_eth_usb_sign_message(keycard_t* kc, apdu_t* cmd) {
uint8_t* segment;
uint32_t len;
uint8_t first_segment;
core_eth_usb_message_reassemble(kc, cmd, &segment, &len, &first_segment);
app_err_t err = core_eth_process_msg(segment, len, first_segment);
switch(err) {
case ERR_OK:
core_eth_usb_sign(kc, cmd);
break;
case ERR_NEED_MORE_DATA:
core_usb_err_sw(cmd, 0x90, 0x00);
break;
case ERR_DATA:
core_usb_err_sw(cmd, 0x6a, 0x80);
break;
case ERR_CANCEL:
core_usb_err_sw(cmd, 0x69, 0x82);
break;
default:
core_usb_err_sw(cmd, 0x6f, 0x00);
break;
}
return err;
}
app_err_t core_eth_usb_sign_eip712(keycard_t* kc, apdu_t* cmd) {
if (APDU_P2(cmd) != 1) {
core_usb_err_sw(cmd, 0x69, 0x82);
return ERR_DATA;
}
uint8_t* segment;
uint32_t len;
uint8_t first_segment;
core_eth_usb_message_reassemble(kc, cmd, &segment, &len, &first_segment);
if ((g_core.data.msg.received + len) > g_core.data.msg.len) {
core_usb_err_sw(cmd, 0x6a, 0x80);
return ERR_DATA;
}
g_core.data.msg.received += len;
core_eth_set_is_message();
if (g_core.data.msg.received == g_core.data.msg.len) {
if (core_eth_process_eip712(g_core.data.msg.content, g_core.data.msg.len) == ERR_OK) {
core_eth_usb_sign(kc, cmd);
return ERR_OK;
} else {
core_usb_err_sw(cmd, 0x69, 0x82);
return ERR_CANCEL;
}
} else {
core_usb_err_sw(cmd, 0x90, 0x00);
return ERR_NEED_MORE_DATA;
}
}
static app_err_t core_eth_eip4527_init_sign(struct eth_sign_request *qr_request) {
app_err_t err = core_set_derivation_path(&qr_request->eth_sign_request_derivation_path);
if (err != ERR_OK) {
return err;
}
uint32_t fingerprint;
err = core_eth_init_sign(&fingerprint);
if (err != ERR_OK) {
return err;
}
if (!(qr_request->eth_sign_request_derivation_path.crypto_keypath_source_fingerprint_present &&
(qr_request->eth_sign_request_derivation_path.crypto_keypath_source_fingerprint.crypto_keypath_source_fingerprint == fingerprint))) {
return ERR_MISMATCH;
}
ethereum_address(g_core.data.key.pub, g_core.address);
return ERR_OK;
}
void core_eth_eip4527_run(struct eth_sign_request* qr_request) {
if (core_eth_eip4527_init_sign(qr_request) != ERR_OK) {
ui_info(LSTR(INFO_WRONG_CARD), 1);
return;
}
app_err_t err;
switch(qr_request->eth_sign_request_data_type.sign_data_type_choice) {
case sign_data_type_eth_transaction_data_m_c:
case sign_data_type_eth_typed_transaction_m_c:
g_core.data.eth_tx.content.data = NULL;
g_core.data.eth_tx.content.chainID = qr_request->eth_sign_request_chain_id_present ? (uint32_t) qr_request->eth_sign_request_chain_id.eth_sign_request_chain_id : 1;
err = core_eth_process_tx(qr_request->eth_sign_request_sign_data.value, qr_request->eth_sign_request_sign_data.len, 1);
break;
case sign_data_type_eth_raw_bytes_m_c:
g_core.data.msg.content = (uint8_t*) qr_request->eth_sign_request_sign_data.value;
g_core.data.msg.len = qr_request->eth_sign_request_sign_data.len;
err = core_eth_process_msg(qr_request->eth_sign_request_sign_data.value, qr_request->eth_sign_request_sign_data.len, 1);
break;
case sign_data_type_eth_typed_data_m_c:
err = core_eth_process_eip712(qr_request->eth_sign_request_sign_data.value, qr_request->eth_sign_request_sign_data.len);
break;
default:
err = ERR_UNSUPPORTED;
break;
}
switch(err) {
case ERR_OK:
break;
case ERR_CANCEL:
return;
default:
ui_info(LSTR(INFO_MALFORMED_DATA), 1);
return;
}
uint32_t v = core_eth_get_tx_v_base();
if (core_eth_sign(&g_core.keycard, g_core.data.sig.plain_sig) != ERR_OK) {
ui_card_transport_error();
return;
}
struct eth_signature sig = {0};
sig.eth_signature_request_id_present = qr_request->eth_sign_request_request_id_present;
if (sig.eth_signature_request_id_present) {
sig.eth_signature_request_id.eth_signature_request_id.value = qr_request->eth_sign_request_request_id.eth_sign_request_request_id.value;
sig.eth_signature_request_id.eth_signature_request_id.len = qr_request->eth_sign_request_request_id.eth_sign_request_request_id.len;
}
sig.eth_signature_signature.value = g_core.data.sig.plain_sig;
sig.eth_signature_signature.len = SIGNATURE_LEN;
v += g_core.data.sig.plain_sig[SIGNATURE_LEN];
if (v <= 0xff) {
g_core.data.sig.plain_sig[SIGNATURE_LEN] = v;
sig.eth_signature_signature.len += 1;
} else if (v <= 0xffff) {
g_core.data.sig.plain_sig[SIGNATURE_LEN] = (v >> 8) & 0xff;
g_core.data.sig.plain_sig[SIGNATURE_LEN + 1] = v & 0xff;
sig.eth_signature_signature.len += 2;
} else if (v <= 0xffffff) {
g_core.data.sig.plain_sig[SIGNATURE_LEN] = (v >> 16) & 0xff;
g_core.data.sig.plain_sig[SIGNATURE_LEN + 1] = (v >> 8) & 0xff;
g_core.data.sig.plain_sig[SIGNATURE_LEN + 2] = v & 0xff;
sig.eth_signature_signature.len += 3;
} else if (v <= 0xffffffff) {
g_core.data.sig.plain_sig[SIGNATURE_LEN] = v >> 24;
g_core.data.sig.plain_sig[SIGNATURE_LEN + 1] = (v >> 16) & 0xff;
g_core.data.sig.plain_sig[SIGNATURE_LEN + 2] = (v >> 8) & 0xff;
g_core.data.sig.plain_sig[SIGNATURE_LEN + 3] = v & 0xff;
sig.eth_signature_signature.len += 4;
}
cbor_encode_eth_signature(g_core.data.sig.cbor_sig, CBOR_SIG_MAX_LEN, &sig, &g_core.data.sig.cbor_len);
ui_display_ur_qr(LSTR(QR_SIGNATURE_TITLE), g_core.data.sig.cbor_sig, g_core.data.sig.cbor_len, ETH_SIGNATURE);
}