mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-25 17:09:44 +00:00
385 lines
12 KiB
C
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();
|
|
}
|