mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-23 23:08:14 +00:00
fsm: Add NEMSignTx
This commit is contained in:
parent
3057f78837
commit
19033a459d
@ -24,6 +24,7 @@ OBJS += signing.o
|
||||
OBJS += crypto.o
|
||||
OBJS += ethereum.o
|
||||
OBJS += ethereum_tokens.o
|
||||
OBJS += nem2.o
|
||||
|
||||
OBJS += debug.o
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include <libopencm3/stm32/flash.h>
|
||||
#include "ethereum.h"
|
||||
#include "nem.h"
|
||||
#include "nem2.h"
|
||||
#include "gettext.h"
|
||||
|
||||
// message methods
|
||||
@ -1144,6 +1145,46 @@ void fsm_msgNEMGetAddress(NEMGetAddress *msg)
|
||||
layoutHome();
|
||||
}
|
||||
|
||||
void fsm_msgNEMSignTx(NEMSignTx *msg) {
|
||||
CHECK_PARAM(msg->has_transaction, _("No common provided"));
|
||||
CHECK_PARAM(msg->has_transfer, _("No transaction provided"));
|
||||
|
||||
const char *reason;
|
||||
CHECK_PARAM(!(reason = nem_validate_common(&msg->transaction)), reason);
|
||||
CHECK_PARAM(!(reason = nem_validate_transfer(&msg->transfer, msg->transaction.network)), reason);
|
||||
|
||||
CHECK_INITIALIZED
|
||||
CHECK_PIN
|
||||
|
||||
if (!nem_askTransfer(&msg->transaction, &msg->transfer)) {
|
||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user"));
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
RESP_INIT(NEMSignedTx);
|
||||
|
||||
HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n, msg->transaction.address_n_count);
|
||||
if (!node) return;
|
||||
|
||||
nem_transaction_ctx context;
|
||||
nem_transaction_start(&context, &node->public_key[1], resp->data.bytes, sizeof(resp->data.bytes));
|
||||
|
||||
if (!nem_fsmTransfer(&context, node, &msg->transaction, &msg->transfer)) {
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
resp->has_data = true;
|
||||
resp->data.size = nem_transaction_end(&context, node->private_key, resp->signature.bytes);
|
||||
|
||||
resp->has_signature = true;
|
||||
resp->signature.size = sizeof(ed25519_signature);
|
||||
|
||||
msg_write(MessageType_MessageType_NEMSignedTx, resp);
|
||||
layoutHome();
|
||||
}
|
||||
|
||||
#if DEBUG_LINK
|
||||
|
||||
void fsm_msgDebugLinkGetState(DebugLinkGetState *msg)
|
||||
|
@ -67,6 +67,7 @@ void fsm_msgEthereumSignMessage(EthereumSignMessage *msg);
|
||||
void fsm_msgEthereumVerifyMessage(EthereumVerifyMessage *msg);
|
||||
|
||||
void fsm_msgNEMGetAddress(NEMGetAddress *msg);
|
||||
void fsm_msgNEMSignTx(NEMSignTx *msg);
|
||||
|
||||
// debug message functions
|
||||
#if DEBUG_LINK
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "qr_encode.h"
|
||||
#include "timer.h"
|
||||
#include "bignum.h"
|
||||
#include "nem.h"
|
||||
#include "secp256k1.h"
|
||||
#include "gettext.h"
|
||||
|
||||
#define BITCOIN_DIVISIBILITY (8)
|
||||
@ -465,3 +467,99 @@ void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appico
|
||||
}
|
||||
layoutDialog(appicon, NULL, verb, NULL, verb, _("U2F security key?"), NULL, appname, NULL, NULL);
|
||||
}
|
||||
|
||||
static inline void nemFormatAmount(bignum256 *amnt, uint64_t quantity, int divisibility, const bignum256 *mul, const char *ticker, char *str_out, size_t size) {
|
||||
bn_read_uint64(quantity, amnt);
|
||||
|
||||
if (mul) {
|
||||
bn_multiply(mul, amnt, &secp256k1.prime);
|
||||
bn_format(amnt, NULL, ticker, divisibility + NEM_XEM_DIVISIBILITY, str_out, size);
|
||||
} else {
|
||||
bn_format(amnt, NULL, ticker, divisibility, str_out, size);
|
||||
}
|
||||
}
|
||||
|
||||
void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *mul, uint64_t fee) {
|
||||
char str_out[32], str_fee[32];
|
||||
bignum256 amnt;
|
||||
|
||||
nemFormatAmount(&amnt, quantity, NEM_XEM_DIVISIBILITY, mul, " " NEM_XEM_TICKER, str_out, sizeof(str_out));
|
||||
|
||||
bn_read_uint64(fee, &amnt);
|
||||
bn_format(&amnt, NULL, " " NEM_XEM_TICKER, NEM_XEM_DIVISIBILITY, str_fee, sizeof(str_fee));
|
||||
layoutDialogSwipe(&bmp_icon_question,
|
||||
_("Cancel"),
|
||||
_("Next"),
|
||||
desc,
|
||||
_("Confirm transfer of"),
|
||||
str_out,
|
||||
_("and network fee of"),
|
||||
str_fee,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void layoutNEMTransferMosaic(const char *namespace, const char *mosaic, uint64_t quantity, const bignum256 *mul) {
|
||||
char mosaic_name[256];
|
||||
strlcpy(mosaic_name, namespace, sizeof(mosaic_name));
|
||||
strlcat(mosaic_name, ".", sizeof(mosaic_name));
|
||||
strlcat(mosaic_name, mosaic, sizeof(mosaic_name));
|
||||
|
||||
char str_out[32];
|
||||
bignum256 amnt;
|
||||
nemFormatAmount(&amnt, quantity, 0, mul, NULL, str_out, sizeof(str_out));
|
||||
|
||||
char *decimal = strchr(str_out, '.');
|
||||
if (decimal != NULL) {
|
||||
*decimal = '\0';
|
||||
}
|
||||
|
||||
layoutDialogSwipe(&bmp_icon_question,
|
||||
_("Cancel"),
|
||||
_("I take the risk"),
|
||||
_("Unknown Mosaic"),
|
||||
_("Confirm transfer of"),
|
||||
str_out,
|
||||
_("raw units of"),
|
||||
mosaic_name,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void layoutNEMTransferPayload(const uint8_t *payload, size_t length, bool encrypted) {
|
||||
if (payload[0] == 0xFE) {
|
||||
char encoded[(length - 1) * 2 + 1];
|
||||
data2hex(&payload[1], length - 1, encoded);
|
||||
|
||||
const char **str = split_message((uint8_t *) encoded, sizeof(encoded) - 1, 16);
|
||||
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"),
|
||||
encrypted ? _("Encrypted hex data") : _("Unencrypted hex data"),
|
||||
str[0], str[1], str[2], str[3], NULL, NULL);
|
||||
} else {
|
||||
const char **str = split_message(payload, length, 16);
|
||||
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"),
|
||||
encrypted ? _("Encrypted message") : _("Unencrypted message"),
|
||||
str[0], str[1], str[2], str[3], NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void layoutNEMTransferTo(const char *desc, const char *address) {
|
||||
static char first_third[NEM_ADDRESS_SIZE / 3 + 1];
|
||||
strlcpy(first_third, address, sizeof(first_third));
|
||||
|
||||
static char second_third[NEM_ADDRESS_SIZE / 3 + 1];
|
||||
strlcpy(second_third, &address[NEM_ADDRESS_SIZE / 3], sizeof(second_third));
|
||||
|
||||
const char *third_third = &address[NEM_ADDRESS_SIZE * 2 / 3];
|
||||
|
||||
layoutDialogSwipe(&bmp_icon_question,
|
||||
_("Cancel"),
|
||||
_("Confirm"),
|
||||
desc,
|
||||
_("Confirm transfer to"),
|
||||
first_third,
|
||||
second_third,
|
||||
third_third,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "layout.h"
|
||||
#include "types.pb.h"
|
||||
#include "bitmaps.h"
|
||||
#include "bignum.h"
|
||||
|
||||
extern void *layoutLast;
|
||||
|
||||
@ -46,5 +47,9 @@ void layoutPublicKey(const uint8_t *pubkey);
|
||||
void layoutSignIdentity(const IdentityType *identity, const char *challenge);
|
||||
void layoutDecryptIdentity(const IdentityType *identity);
|
||||
void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appicon);
|
||||
void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *mul, uint64_t fee);
|
||||
void layoutNEMTransferMosaic(const char *namespace, const char *mosaic, uint64_t quantity, const bignum256 *mul);
|
||||
void layoutNEMTransferPayload(const uint8_t *payload, size_t length, bool encrypted);
|
||||
void layoutNEMTransferTo(const char *desc, const char *address);
|
||||
|
||||
#endif
|
||||
|
212
firmware/nem2.c
Normal file
212
firmware/nem2.c
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project.
|
||||
*
|
||||
* Copyright (C) 2017 Saleem Rashid <dev@saleemrashid.com>
|
||||
*
|
||||
* 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 "nem2.h"
|
||||
|
||||
#include "aes.h"
|
||||
#include "fsm.h"
|
||||
#include "gettext.h"
|
||||
#include "layout2.h"
|
||||
#include "protect.h"
|
||||
#include "rng.h"
|
||||
|
||||
const char *nem_validate_common(NEMTransactionCommon *common) {
|
||||
if (!common->has_network) {
|
||||
common->has_network = true;
|
||||
common->network = NEM_NETWORK_MAINNET;
|
||||
}
|
||||
|
||||
if (nem_network_name(common->network) == NULL) return _("Invalid NEM network");
|
||||
if (!common->has_timestamp) return _("No timestamp provided");
|
||||
if (!common->has_fee) return _("No fee provided");
|
||||
if (!common->has_deadline) return _("No deadline provided");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *nem_validate_transfer(const NEMTransfer *transfer, uint8_t network) {
|
||||
if (!transfer->has_recipient) return _("No recipient provided");
|
||||
if (!transfer->has_amount) return _("No amount provided");
|
||||
if (transfer->has_public_key && transfer->public_key.size != 32) return _("Invalid recipient public key");
|
||||
|
||||
if (!nem_validate_address(transfer->recipient, network)) return _("Invalid recipient address");
|
||||
|
||||
for (size_t i = 0; i < transfer->mosaics_count; i++) {
|
||||
const NEMMosaic *mosaic = &transfer->mosaics[i];
|
||||
|
||||
if (!mosaic->has_namespace) return "No mosaic namespace provided";
|
||||
if (!mosaic->has_mosaic) return "No mosaic name provided";
|
||||
if (!mosaic->has_quantity) return "No mosaic quantity provided";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *transfer) {
|
||||
const char *network = nem_network_name(common->network);
|
||||
if (network == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (transfer->mosaics_count) {
|
||||
bool done[transfer->mosaics_count];
|
||||
memset(done, 0, sizeof(done));
|
||||
|
||||
uint64_t quantity[transfer->mosaics_count];
|
||||
uint64_t *xemQuantity = NULL;
|
||||
|
||||
bignum256 mul;
|
||||
bn_read_uint64(transfer->amount, &mul);
|
||||
|
||||
for (size_t i = 0; i < transfer->mosaics_count; i++) {
|
||||
// Skip duplicate mosaics
|
||||
if (done[i]) continue;
|
||||
|
||||
const NEMMosaic *mosaic = &transfer->mosaics[i];
|
||||
|
||||
// XEM is treated specially
|
||||
if (strcmp(mosaic->namespace, "nem") == 0 && strcmp(mosaic->mosaic, "xem") == 0) {
|
||||
done[i] = true;
|
||||
xemQuantity = &quantity[i];
|
||||
}
|
||||
|
||||
quantity[i] = mosaic->quantity;
|
||||
for (size_t j = i + 1; j < transfer->mosaics_count; j++) {
|
||||
const NEMMosaic *new_mosaic = &transfer->mosaics[j];
|
||||
|
||||
if (strcmp(mosaic->namespace, new_mosaic->namespace) == 0 && strcmp(mosaic->mosaic, new_mosaic->mosaic) == 0) {
|
||||
// Duplicate mosaics are merged into one
|
||||
done[i] = true;
|
||||
quantity[i] += transfer->mosaics[j].quantity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layoutNEMTransferXEM(network, xemQuantity == NULL ? 0 : *xemQuantity, &mul, common->fee);
|
||||
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < transfer->mosaics_count; i++) {
|
||||
// Skip special or duplicate mosaics
|
||||
if (done[i]) continue;
|
||||
|
||||
const NEMMosaic *mosaic = &transfer->mosaics[i];
|
||||
|
||||
layoutNEMTransferMosaic(mosaic->namespace, mosaic->mosaic, quantity[i], &mul);
|
||||
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
layoutNEMTransferXEM(network, transfer->amount, NULL, common->fee);
|
||||
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (transfer->has_payload) {
|
||||
layoutNEMTransferPayload(transfer->payload.bytes, transfer->payload.size, transfer->has_public_key);
|
||||
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
layoutNEMTransferTo(network, transfer->recipient);
|
||||
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node, const NEMTransactionCommon *common, const NEMTransfer *transfer) {
|
||||
static uint8_t encrypted[NEM_ENCRYPTED_PAYLOAD_SIZE(sizeof(transfer->payload.bytes))];
|
||||
|
||||
const uint8_t *payload = transfer->payload.bytes;
|
||||
size_t size = transfer->payload.size;
|
||||
|
||||
if (transfer->has_public_key) {
|
||||
if (node == NULL) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError, _("Private key unavailable for encrypted message"));
|
||||
return false;
|
||||
}
|
||||
|
||||
random_buffer(encrypted, NEM_SALT_SIZE + AES_BLOCK_SIZE);
|
||||
|
||||
// hdnode_nem_encrypt mutates the IV
|
||||
uint8_t iv[AES_BLOCK_SIZE];
|
||||
memcpy(iv, &encrypted[NEM_SALT_SIZE], AES_BLOCK_SIZE);
|
||||
|
||||
const uint8_t *salt = encrypted;
|
||||
uint8_t *buffer = &encrypted[NEM_SALT_SIZE + AES_BLOCK_SIZE];
|
||||
|
||||
bool ret = hdnode_nem_encrypt(node,
|
||||
transfer->public_key.bytes,
|
||||
iv,
|
||||
salt,
|
||||
payload,
|
||||
size,
|
||||
buffer);
|
||||
|
||||
if (!ret) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to encrypt payload"));
|
||||
return false;
|
||||
}
|
||||
|
||||
payload = encrypted;
|
||||
size = NEM_ENCRYPTED_PAYLOAD_SIZE(size);
|
||||
}
|
||||
|
||||
bool ret = nem_transaction_create_transfer(context,
|
||||
common->network,
|
||||
common->timestamp,
|
||||
NULL,
|
||||
common->fee,
|
||||
common->deadline,
|
||||
transfer->recipient,
|
||||
transfer->amount,
|
||||
payload,
|
||||
size,
|
||||
transfer->has_public_key,
|
||||
transfer->mosaics_count);
|
||||
|
||||
if (!ret) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to create transfer transaction"));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < transfer->mosaics_count; i++) {
|
||||
const NEMMosaic *mosaic = &transfer->mosaics[i];
|
||||
|
||||
ret = nem_transaction_write_mosaic(context,
|
||||
mosaic->namespace,
|
||||
mosaic->mosaic,
|
||||
mosaic->quantity);
|
||||
|
||||
if (!ret) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError, "Failed to attach mosaics");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
36
firmware/nem2.h
Normal file
36
firmware/nem2.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project.
|
||||
*
|
||||
* Copyright (C) 2017 Saleem Rashid <dev@saleemrashid.com>
|
||||
*
|
||||
* 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 __NEM2_H__
|
||||
#define __NEM2_H__
|
||||
|
||||
#include "nem.h"
|
||||
|
||||
#include "messages.pb.h"
|
||||
#include "types.pb.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
const char *nem_validate_common(NEMTransactionCommon *common);
|
||||
const char *nem_validate_transfer(const NEMTransfer *transfer, uint8_t network);
|
||||
|
||||
bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *transfer);
|
||||
bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node, const NEMTransactionCommon *common, const NEMTransfer *transfer);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user