From 563723a55ffa1c5533dd1f6f2ad8056aa815ece5 Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Sat, 1 Jul 2017 14:38:04 +0100 Subject: [PATCH] nem2: Handle multisig transactions --- firmware/fsm.c | 54 +++++++++++++++++++-- firmware/layout2.c | 45 ++++++++++++++++++ firmware/layout2.h | 3 +- firmware/nem2.c | 114 +++++++++++++++++++++++++++++++++++++-------- firmware/nem2.h | 10 +++- 5 files changed, 198 insertions(+), 28 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index 15395425ed..fff56caed7 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -1149,14 +1149,39 @@ void fsm_msgNEMSignTx(NEMSignTx *msg) { CHECK_PARAM(msg->has_transaction, _("No common provided")); CHECK_PARAM(msg->has_transfer, _("No transaction provided")); + bool cosigning = msg->has_cosigning && msg->cosigning; + const char *reason; - CHECK_PARAM(!(reason = nem_validate_common(&msg->transaction)), reason); + CHECK_PARAM(!(reason = nem_validate_common(&msg->transaction, false)), reason); CHECK_PARAM(!(reason = nem_validate_transfer(&msg->transfer, msg->transaction.network)), reason); + if (msg->has_multisig) { + CHECK_PARAM(!(reason = nem_validate_common(&msg->multisig, true)), reason); + + CHECK_PARAM(msg->transaction.network == msg->multisig.network, _("Inner transaction network is different")); + } else { + CHECK_PARAM(!cosigning, _("No multisig transaction to cosign")); + } + CHECK_INITIALIZED CHECK_PIN - if (!nem_askTransfer(&msg->transaction, &msg->transfer)) { + const char *network = nem_network_name(msg->transaction.network); + + if (msg->has_multisig) { + char address[NEM_ADDRESS_SIZE + 1]; + nem_get_address(msg->multisig.signer.bytes, msg->multisig.network, address); + + if (!nem_askMultisig(address, network, cosigning, msg->transaction.fee)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user")); + layoutHome(); + return; + } + } + + const NEMTransactionCommon *common = msg->has_multisig ? &msg->multisig : &msg->transaction; + + if (msg->has_transfer && !nem_askTransfer(common, &msg->transfer, network)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user")); layoutHome(); return; @@ -1167,12 +1192,31 @@ void fsm_msgNEMSignTx(NEMSignTx *msg) { HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n, msg->transaction.address_n_count); if (!node) return; + hdnode_fill_public_key(node); + 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; + if (msg->has_multisig) { + uint8_t buffer[sizeof(resp->data.bytes)]; + + nem_transaction_ctx inner; + nem_transaction_start(&inner, msg->multisig.signer.bytes, buffer, sizeof(buffer)); + + if (msg->has_transfer && !nem_fsmTransfer(&inner, NULL, &msg->multisig, &msg->transfer)) { + layoutHome(); + return; + } + + if (!nem_fsmMultisig(&context, &msg->transaction, &inner, cosigning)) { + layoutHome(); + return; + } + } else { + if (msg->has_transfer && !nem_fsmTransfer(&context, node, &msg->transaction, &msg->transfer)) { + layoutHome(); + return; + } } resp->has_data = true; diff --git a/firmware/layout2.c b/firmware/layout2.c index ccd9eda6c0..d6e50c4ce8 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -479,6 +479,27 @@ static inline void nemFormatAmount(bignum256 *amnt, uint64_t quantity, int divis } } +void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *address, const char *line5, const char *line6) { + 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(icon, + btnNo, + btnYes, + desc, + line1, + first_third, + second_third, + third_third, + line5, + line6); +} + void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *mul, uint64_t fee) { char str_out[32], str_fee[32]; bignum256 amnt; @@ -499,6 +520,30 @@ void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 * NULL); } +void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc, uint64_t fee1, const char *fee2_desc, uint64_t fee2) { + char str_fee1[32], str_fee2[32]; + bignum256 amnt; + + bn_read_uint64(fee1, &amnt); + bn_format(&amnt, NULL, " " NEM_XEM_TICKER, NEM_XEM_DIVISIBILITY, str_fee1, sizeof(str_fee1)); + + if (fee2_desc) { + bn_read_uint64(fee2, &amnt); + bn_format(&amnt, NULL, " " NEM_XEM_TICKER, NEM_XEM_DIVISIBILITY, str_fee2, sizeof(str_fee2)); + } + + layoutDialogSwipe(&bmp_icon_question, + _("Cancel"), + confirm ? _("Confirm") : _("Next"), + desc, + fee1_desc, + str_fee1, + fee2_desc, + fee2_desc ? str_fee2 : NULL, + 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)); diff --git a/firmware/layout2.h b/firmware/layout2.h index 6364e05416..aa6675f0ad 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -47,9 +47,10 @@ 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 layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *address, const char *line5, const char *line6); void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *mul, uint64_t fee); +void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc, uint64_t fee1, const char *fee2_desc, uint64_t fee2); 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 diff --git a/firmware/nem2.c b/firmware/nem2.c index 62d92549ff..6d9505ac3e 100644 --- a/firmware/nem2.c +++ b/firmware/nem2.c @@ -26,16 +26,35 @@ #include "protect.h" #include "rng.h" -const char *nem_validate_common(NEMTransactionCommon *common) { +const char *nem_validate_common(NEMTransactionCommon *common, bool inner) { 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"); + if (nem_network_name(common->network) == NULL) { + return inner ? _("Invalid NEM network in inner transaction") : _("Invalid NEM network"); + } + + if (!common->has_timestamp) { + return inner ? _("No timestamp provided in inner transaction") : _("No timestamp provided"); + } + + if (!common->has_fee) { + return inner ? _("No fee provided in inner transaction") : _("No fee provided"); + } + + if (!common->has_deadline) { + return inner ? _("No deadline provided in inner transaction") : _("No deadline provided"); + } + + if (inner != common->has_signer) { + return inner ? _("No signer provided in inner transaction") : _("Signer not allowed in outer transaction"); + } + + if (common->has_signer && common->signer.size != sizeof(ed25519_public_key)) { + return _("Invalid signer public key in inner transaction"); + } return NULL; } @@ -43,29 +62,26 @@ const char *nem_validate_common(NEMTransactionCommon *common) { 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 (transfer->has_public_key && transfer->public_key.size != sizeof(ed25519_public_key)) { + 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"; + 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; - } - - +bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *transfer, const char *desc) { if (transfer->mosaics_count) { bool done[transfer->mosaics_count]; memset(done, 0, sizeof(done)); @@ -100,7 +116,7 @@ bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *tran } } - layoutNEMTransferXEM(network, xemQuantity == NULL ? 0 : *xemQuantity, &mul, common->fee); + layoutNEMTransferXEM(desc, xemQuantity == NULL ? 0 : *xemQuantity, &mul, common->fee); if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { return false; } @@ -117,7 +133,7 @@ bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *tran } } } else { - layoutNEMTransferXEM(network, transfer->amount, NULL, common->fee); + layoutNEMTransferXEM(desc, transfer->amount, NULL, common->fee); if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { return false; } @@ -130,7 +146,14 @@ bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *tran } } - layoutNEMTransferTo(network, transfer->recipient); + layoutNEMDialog(&bmp_icon_question, + _("Cancel"), + _("Confirm"), + desc, + _("Confirm transfer to"), + transfer->recipient, + NULL, + NULL); if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { return false; } @@ -203,10 +226,61 @@ bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node, const NEM mosaic->quantity); if (!ret) { - fsm_sendFailure(FailureType_Failure_ProcessError, "Failed to attach mosaics"); + fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to attach mosaics")); return false; } } return true; } + +bool nem_askMultisig(const char *address, const char *desc, bool cosigning, uint64_t fee) { + layoutNEMDialog(&bmp_icon_question, + _("Cancel"), + _("Next"), + desc, + cosigning ? _("Cosign transaction for") : _("Initiate transaction for"), + address, + NULL, + NULL); + + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + layoutNEMNetworkFee(desc, false, _("Confirm multisig fee"), fee, NULL, 0); + + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + return true; +} + +bool nem_fsmMultisig(nem_transaction_ctx *context, const NEMTransactionCommon *common, const nem_transaction_ctx *inner, bool cosigning) { + bool ret; + if (cosigning) { + ret = nem_transaction_create_multisig_signature(context, + common->network, + common->timestamp, + NULL, + common->fee, + common->deadline, + inner); + } else { + ret = nem_transaction_create_multisig(context, + common->network, + common->timestamp, + NULL, + common->fee, + common->deadline, + inner); + } + + if (!ret) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to create multisig transaction")); + return false; + } + + return true; +} diff --git a/firmware/nem2.h b/firmware/nem2.h index ed394fdab7..0c87fe7a18 100644 --- a/firmware/nem2.h +++ b/firmware/nem2.h @@ -27,10 +27,16 @@ #include -const char *nem_validate_common(NEMTransactionCommon *common); +const char *nem_validate_common(NEMTransactionCommon *common, bool inner); const char *nem_validate_transfer(const NEMTransfer *transfer, uint8_t network); -bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *transfer); +bool nem_askTransaction(const char *desc, const NEMTransactionCommon *common, const NEMSignTx *msg); +bool nem_fsmTransaction(nem_transaction_ctx *context, const HDNode *node, const NEMTransactionCommon *common, const NEMSignTx *msg); + +bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *transfer, const char *desc); bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node, const NEMTransactionCommon *common, const NEMTransfer *transfer); +bool nem_askMultisig(const char *address, const char *desc, bool cosigning, uint64_t fee); +bool nem_fsmMultisig(nem_transaction_ctx *context, const NEMTransactionCommon *common, const nem_transaction_ctx *inner, bool cosigning); + #endif