1
0
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:
Saleem Rashid 2017-05-30 17:41:02 +01:00
parent 3057f78837
commit 19033a459d
7 changed files with 394 additions and 0 deletions

View File

@ -24,6 +24,7 @@ OBJS += signing.o
OBJS += crypto.o
OBJS += ethereum.o
OBJS += ethereum_tokens.o
OBJS += nem2.o
OBJS += debug.o

View File

@ -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)

View File

@ -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

View File

@ -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);
}

View File

@ -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
View 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
View 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