1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-25 00:48:19 +00:00
trezor-firmware/legacy/firmware/fsm_msg_nem.h

385 lines
12 KiB
C

/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@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/>.
*/
static bool fsm_nemCheckPath(uint32_t address_n_count,
const uint32_t *address_n, uint8_t network) {
if (nem_path_check(address_n_count, address_n, network, true)) {
return true;
}
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict &&
!nem_path_check(address_n_count, address_n, network, false)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
return false;
}
return fsm_layoutPathWarning();
}
void fsm_msgNEMGetAddress(NEMGetAddress *msg) {
if (!msg->has_network) {
msg->network = NEM_NETWORK_MAINNET;
}
const char *network;
CHECK_PARAM((network = nem_network_name(msg->network)),
_("Invalid NEM network"));
CHECK_INITIALIZED
CHECK_PIN
RESP_INIT(NEMAddress);
if (!fsm_nemCheckPath(msg->address_n_count, msg->address_n, msg->network)) {
layoutHome();
return;
}
HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
if (!hdnode_get_nem_address(node, msg->network, resp->address)) {
layoutHome();
return;
}
if (msg->has_show_display && msg->show_display) {
char desc[16];
strlcpy(desc, network, sizeof(desc));
strlcat(desc, ":", sizeof(desc));
if (!fsm_layoutAddress(resp->address, desc, true, 0, msg->address_n,
msg->address_n_count, false, NULL, 0, 0, NULL)) {
return;
}
}
msg_write(MessageType_MessageType_NEMAddress, resp);
layoutHome();
}
void fsm_msgNEMSignTx(NEMSignTx *msg) {
const char *reason;
#define NEM_CHECK_PARAM(s) CHECK_PARAM((reason = (s)) == NULL, reason)
#define NEM_CHECK_PARAM_WHEN(b, s) \
CHECK_PARAM(!(b) || (reason = (s)) == NULL, reason)
// Ensure exactly one transaction is provided
unsigned int provided = msg->has_transfer + msg->has_provision_namespace +
msg->has_mosaic_creation + msg->has_supply_change +
msg->has_aggregate_modification +
msg->has_importance_transfer;
CHECK_PARAM(provided != 0, _("No transaction provided"));
CHECK_PARAM(provided == 1, _("More than one transaction provided"));
NEM_CHECK_PARAM(nem_validate_common(&msg->transaction, false));
NEM_CHECK_PARAM_WHEN(
msg->has_transfer,
nem_validate_transfer(&msg->transfer, msg->transaction.network));
NEM_CHECK_PARAM_WHEN(
msg->has_provision_namespace,
nem_validate_provision_namespace(&msg->provision_namespace,
msg->transaction.network));
NEM_CHECK_PARAM_WHEN(msg->has_mosaic_creation,
nem_validate_mosaic_creation(&msg->mosaic_creation,
msg->transaction.network));
NEM_CHECK_PARAM_WHEN(msg->has_supply_change,
nem_validate_supply_change(&msg->supply_change));
NEM_CHECK_PARAM_WHEN(msg->has_aggregate_modification,
nem_validate_aggregate_modification(
&msg->aggregate_modification, !msg->has_multisig));
NEM_CHECK_PARAM_WHEN(
msg->has_importance_transfer,
nem_validate_importance_transfer(&msg->importance_transfer));
bool cosigning = msg->has_cosigning && msg->cosigning;
if (msg->has_multisig) {
NEM_CHECK_PARAM(nem_validate_common(&msg->multisig, true));
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
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;
}
}
RESP_INIT(NEMSignedTx);
if (!fsm_nemCheckPath(msg->transaction.address_n_count,
msg->transaction.address_n, msg->transaction.network)) {
layoutHome();
return;
}
HDNode *node =
fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n,
msg->transaction.address_n_count, NULL);
if (!node) return;
if (hdnode_fill_public_key(node) != 0) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to derive public key"));
layoutHome();
return;
}
const NEMTransactionCommon *common =
msg->has_multisig ? &msg->multisig : &msg->transaction;
char address[NEM_ADDRESS_SIZE + 1];
hdnode_get_nem_address(node, common->network, address);
if (msg->has_transfer) {
nem_canonicalizeMosaics(&msg->transfer);
}
if (msg->has_transfer && !nem_askTransfer(common, &msg->transfer, network)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_provision_namespace &&
!nem_askProvisionNamespace(common, &msg->provision_namespace, network)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_mosaic_creation &&
!nem_askMosaicCreation(common, &msg->mosaic_creation, network, address)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_supply_change &&
!nem_askSupplyChange(common, &msg->supply_change, network)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_aggregate_modification &&
!nem_askAggregateModification(common, &msg->aggregate_modification,
network, !msg->has_multisig)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_importance_transfer &&
!nem_askImportanceTransfer(common, &msg->importance_transfer, network)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
nem_transaction_ctx context;
nem_transaction_start(&context, &node->public_key[1], resp->data.bytes,
sizeof(resp->data.bytes));
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 (msg->has_provision_namespace &&
!nem_fsmProvisionNamespace(&inner, &msg->multisig,
&msg->provision_namespace)) {
layoutHome();
return;
}
if (msg->has_mosaic_creation &&
!nem_fsmMosaicCreation(&inner, &msg->multisig, &msg->mosaic_creation)) {
layoutHome();
return;
}
if (msg->has_supply_change &&
!nem_fsmSupplyChange(&inner, &msg->multisig, &msg->supply_change)) {
layoutHome();
return;
}
if (msg->has_aggregate_modification &&
!nem_fsmAggregateModification(&inner, &msg->multisig,
&msg->aggregate_modification)) {
layoutHome();
return;
}
if (msg->has_importance_transfer &&
!nem_fsmImportanceTransfer(&inner, &msg->multisig,
&msg->importance_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;
}
if (msg->has_provision_namespace &&
!nem_fsmProvisionNamespace(&context, &msg->transaction,
&msg->provision_namespace)) {
layoutHome();
return;
}
if (msg->has_mosaic_creation &&
!nem_fsmMosaicCreation(&context, &msg->transaction,
&msg->mosaic_creation)) {
layoutHome();
return;
}
if (msg->has_supply_change &&
!nem_fsmSupplyChange(&context, &msg->transaction,
&msg->supply_change)) {
layoutHome();
return;
}
if (msg->has_aggregate_modification &&
!nem_fsmAggregateModification(&context, &msg->transaction,
&msg->aggregate_modification)) {
layoutHome();
return;
}
if (msg->has_importance_transfer &&
!nem_fsmImportanceTransfer(&context, &msg->transaction,
&msg->importance_transfer)) {
layoutHome();
return;
}
}
resp->data.size =
nem_transaction_end(&context, node->private_key, resp->signature.bytes);
resp->signature.size = sizeof(ed25519_signature);
msg_write(MessageType_MessageType_NEMSignedTx, resp);
layoutHome();
}
void fsm_msgNEMDecryptMessage(NEMDecryptMessage *msg) {
RESP_INIT(NEMDecryptedMessage);
CHECK_INITIALIZED
CHECK_PARAM(nem_network_name(msg->network), _("Invalid NEM network"));
CHECK_PARAM(msg->has_payload, _("No payload provided"));
CHECK_PARAM(msg->payload.size >= NEM_ENCRYPTED_PAYLOAD_SIZE(0),
_("Invalid encrypted payload"));
CHECK_PARAM(msg->has_public_key, _("No public key provided"));
CHECK_PARAM(msg->public_key.size == 32, _("Invalid public key"));
CHECK_PIN
char address[NEM_ADDRESS_SIZE + 1];
nem_get_address(msg->public_key.bytes, msg->network, address);
layoutNEMDialog(&bmp_icon_question, _("Cancel"), _("Confirm"),
_("Decrypt message"), _("Confirm address?"), address);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
if (!fsm_nemCheckPath(msg->address_n_count, msg->address_n, msg->network)) {
layoutHome();
return;
}
const HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
const uint8_t *salt = msg->payload.bytes;
uint8_t *iv = &msg->payload.bytes[NEM_SALT_SIZE];
const uint8_t *payload = &msg->payload.bytes[NEM_SALT_SIZE + AES_BLOCK_SIZE];
size_t size = msg->payload.size - NEM_SALT_SIZE - AES_BLOCK_SIZE;
// hdnode_nem_decrypt mutates the IV, so this will modify msg
bool ret = hdnode_nem_decrypt(node, msg->public_key.bytes, iv, salt, payload,
size, resp->payload.bytes);
if (!ret) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to decrypt payload"));
layoutHome();
return;
}
resp->payload.size = NEM_DECRYPTED_SIZE(resp->payload.bytes, size);
layoutNEMTransferPayload(resp->payload.bytes, resp->payload.size, true);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
msg_write(MessageType_MessageType_NEMDecryptedMessage, resp);
layoutHome();
}