diff --git a/firmware/fsm.c b/firmware/fsm.c index 9e150eebd1..c707600e20 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -239,1773 +239,10 @@ static bool fsm_layoutAddress(const char *address, const char *desc, bool ignore } } -void fsm_msgInitialize(Initialize *msg) -{ - recovery_abort(); - signing_abort(); - if (msg && msg->has_state && msg->state.size == 64) { - uint8_t i_state[64]; - if (!session_getState(msg->state.bytes, i_state, NULL)) { - session_clear(false); // do not clear PIN - } else { - if (0 != memcmp(msg->state.bytes, i_state, 64)) { - session_clear(false); // do not clear PIN - } - } - } else { - session_clear(false); // do not clear PIN - } - layoutHome(); - fsm_msgGetFeatures(0); -} - -void fsm_msgGetFeatures(GetFeatures *msg) -{ - (void)msg; - RESP_INIT(Features); - resp->has_vendor = true; strlcpy(resp->vendor, "bitcointrezor.com", sizeof(resp->vendor)); - resp->has_major_version = true; resp->major_version = VERSION_MAJOR; - resp->has_minor_version = true; resp->minor_version = VERSION_MINOR; - resp->has_patch_version = true; resp->patch_version = VERSION_PATCH; - resp->has_device_id = true; strlcpy(resp->device_id, storage_uuid_str, sizeof(resp->device_id)); - resp->has_pin_protection = true; resp->pin_protection = storage_hasPin(); - resp->has_passphrase_protection = true; resp->passphrase_protection = storage_hasPassphraseProtection(); -#ifdef SCM_REVISION - int len = sizeof(SCM_REVISION) - 1; - resp->has_revision = true; memcpy(resp->revision.bytes, SCM_REVISION, len); resp->revision.size = len; -#endif - resp->has_bootloader_hash = true; resp->bootloader_hash.size = memory_bootloader_hash(resp->bootloader_hash.bytes); - if (storage_getLanguage()) { - resp->has_language = true; - strlcpy(resp->language, storage_getLanguage(), sizeof(resp->language)); - } - if (storage_getLabel()) { - resp->has_label = true; - strlcpy(resp->label, storage_getLabel(), sizeof(resp->label)); - } - - _Static_assert(pb_arraysize(Features, coins) >= COINS_COUNT, "Features.coins max_count not large enough"); - - resp->coins_count = COINS_COUNT; - for (int i = 0; i < COINS_COUNT; i++) { - if (coins[i].coin_name) { - resp->coins[i].has_coin_name = true; - strlcpy(resp->coins[i].coin_name, coins[i].coin_name, sizeof(resp->coins[i].coin_name)); - } - if (coins[i].coin_shortcut) { - resp->coins[i].has_coin_shortcut = true; - strlcpy(resp->coins[i].coin_shortcut, coins[i].coin_shortcut + 1, sizeof(resp->coins[i].coin_shortcut)); - } - resp->coins[i].has_address_type = coins[i].has_address_type; - resp->coins[i].address_type = coins[i].address_type; - resp->coins[i].has_maxfee_kb = true; - resp->coins[i].maxfee_kb = coins[i].maxfee_kb; - resp->coins[i].has_address_type_p2sh = coins[i].has_address_type_p2sh; - resp->coins[i].address_type_p2sh = coins[i].address_type_p2sh; - resp->coins[i].has_xpub_magic = coins[i].xpub_magic != 0; - resp->coins[i].xpub_magic = coins[i].xpub_magic; - resp->coins[i].has_xprv_magic = coins[i].xprv_magic != 0; - resp->coins[i].xprv_magic = coins[i].xprv_magic; - resp->coins[i].has_segwit = true; - resp->coins[i].segwit = coins[i].has_segwit; - resp->coins[i].has_forkid = coins[i].has_forkid; - resp->coins[i].forkid = coins[i].forkid; - resp->coins[i].has_force_bip143 = true; - resp->coins[i].force_bip143 = coins[i].force_bip143; - } - resp->has_initialized = true; resp->initialized = storage_isInitialized(); - resp->has_imported = true; resp->imported = storage_isImported(); - resp->has_pin_cached = true; resp->pin_cached = session_isPinCached(); - resp->has_passphrase_cached = true; resp->passphrase_cached = session_isPassphraseCached(); - resp->has_needs_backup = true; resp->needs_backup = storage_needsBackup(); - resp->unfinished_backup = true; resp->unfinished_backup = storage_unfinishedBackup(); - resp->has_flags = true; resp->flags = storage_getFlags(); - resp->has_model = true; strlcpy(resp->model, "1", sizeof(resp->model)); - - msg_write(MessageType_MessageType_Features, resp); -} - -void fsm_msgPing(Ping *msg) -{ - RESP_INIT(Success); - - if (msg->has_button_protection && msg->button_protection) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("answer to ping?"), NULL, NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - if (msg->has_pin_protection && msg->pin_protection) { - CHECK_PIN - } - - if (msg->has_passphrase_protection && msg->passphrase_protection) { - if (!protectPassphrase()) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - return; - } - } - - if (msg->has_message) { - resp->has_message = true; - memcpy(&(resp->message), &(msg->message), sizeof(resp->message)); - } - msg_write(MessageType_MessageType_Success, resp); - layoutHome(); -} - -void fsm_msgChangePin(ChangePin *msg) -{ - bool removal = msg->has_remove && msg->remove; - if (removal) { - if (storage_hasPin()) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("remove current PIN?"), NULL, NULL, NULL, NULL); - } else { - fsm_sendSuccess(_("PIN removed")); - return; - } - } else { - if (storage_hasPin()) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change current PIN?"), NULL, NULL, NULL, NULL); - } else { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("set new PIN?"), NULL, NULL, NULL, NULL); - } - } - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN_UNCACHED - - if (removal) { - storage_setPin(""); - storage_update(); - fsm_sendSuccess(_("PIN removed")); - } else { - if (protectChangePin()) { - fsm_sendSuccess(_("PIN changed")); - } else { - fsm_sendFailure(FailureType_Failure_PinMismatch, NULL); - } - } - layoutHome(); -} - -void fsm_msgWipeDevice(WipeDevice *msg) -{ - (void)msg; - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("wipe the device?"), NULL, _("All data will be lost."), NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_WipeDevice, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - storage_wipe(); - // the following does not work on Mac anyway :-/ Linux/Windows are fine, so it is not needed - // usbReconnect(); // force re-enumeration because of the serial number change - fsm_sendSuccess(_("Device wiped")); - layoutHome(); -} - -void fsm_msgGetEntropy(GetEntropy *msg) -{ -#if !DEBUG_RNG - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("send entropy?"), NULL, NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } -#endif - RESP_INIT(Entropy); - uint32_t len = msg->size; - if (len > 1024) { - len = 1024; - } - resp->entropy.size = len; - random_buffer(resp->entropy.bytes, len); - msg_write(MessageType_MessageType_Entropy, resp); - layoutHome(); -} - -void fsm_msgGetPublicKey(GetPublicKey *msg) -{ - RESP_INIT(PublicKey); - - CHECK_INITIALIZED - - CHECK_PIN - - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - - const char *curve = coin->curve_name; - if (msg->has_ecdsa_curve_name) { - curve = msg->ecdsa_curve_name; - } - uint32_t fingerprint; - HDNode *node = node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count, &fingerprint); - if (!node) return; - hdnode_fill_public_key(node); - - if (msg->has_show_display && msg->show_display) { - layoutPublicKey(node->public_key); - if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - resp->node.depth = node->depth; - resp->node.fingerprint = fingerprint; - resp->node.child_num = node->child_num; - resp->node.chain_code.size = 32; - memcpy(resp->node.chain_code.bytes, node->chain_code, 32); - resp->node.has_private_key = false; - resp->node.has_public_key = true; - resp->node.public_key.size = 33; - memcpy(resp->node.public_key.bytes, node->public_key, 33); - if (node->public_key[0] == 1) { - /* ed25519 public key */ - resp->node.public_key.bytes[0] = 0; - } - resp->has_xpub = true; - hdnode_serialize_public(node, fingerprint, coin->xpub_magic, resp->xpub, sizeof(resp->xpub)); - msg_write(MessageType_MessageType_PublicKey, resp); - layoutHome(); -} - -void fsm_msgLoadDevice(LoadDevice *msg) -{ - CHECK_NOT_INITIALIZED - - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("I take the risk"), NULL, _("Loading private seed"), _("is not recommended."), _("Continue only if you"), _("know what you are"), _("doing!"), NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - if (msg->has_mnemonic && !(msg->has_skip_checksum && msg->skip_checksum) ) { - if (!mnemonic_check(msg->mnemonic)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Mnemonic with wrong checksum provided")); - layoutHome(); - return; - } - } - - storage_loadDevice(msg); - fsm_sendSuccess(_("Device loaded")); - layoutHome(); -} - -void fsm_msgResetDevice(ResetDevice *msg) -{ - CHECK_NOT_INITIALIZED - - CHECK_PARAM(!msg->has_strength || msg->strength == 128 || msg->strength == 192 || msg->strength == 256, _("Invalid seed strength")); - - reset_init( - msg->has_display_random && msg->display_random, - msg->has_strength ? msg->strength : 128, - msg->has_passphrase_protection && msg->passphrase_protection, - msg->has_pin_protection && msg->pin_protection, - msg->has_language ? msg->language : 0, - msg->has_label ? msg->label : 0, - msg->has_u2f_counter ? msg->u2f_counter : 0, - msg->has_skip_backup ? msg->skip_backup : false - ); -} - -void fsm_msgBackupDevice(BackupDevice *msg) -{ - CHECK_INITIALIZED - - CHECK_PIN_UNCACHED - - (void)msg; - reset_backup(true); -} - -void fsm_msgSignTx(SignTx *msg) -{ - CHECK_INITIALIZED - - CHECK_PARAM(msg->inputs_count > 0, _("Transaction must have at least one input")); - CHECK_PARAM(msg->outputs_count > 0, _("Transaction must have at least one output")); - CHECK_PARAM(msg->inputs_count + msg->outputs_count >= msg->inputs_count, _("Value overflow")); - - CHECK_PIN - - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - const HDNode *node = fsm_getDerivedNode(coin->curve_name, NULL, 0, NULL); - if (!node) return; - - signing_init(msg, coin, node); -} - -void fsm_msgTxAck(TxAck *msg) -{ - CHECK_PARAM(msg->has_tx, _("No transaction provided")); - - signing_txack(&(msg->tx)); -} - -void fsm_msgCancel(Cancel *msg) -{ - (void)msg; - recovery_abort(); - signing_abort(); - ethereum_signing_abort(); - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); -} - -void fsm_msgEthereumSignTx(EthereumSignTx *msg) -{ - CHECK_INITIALIZED - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - ethereum_signing_init(msg, node); -} - -void fsm_msgEthereumTxAck(EthereumTxAck *msg) -{ - ethereum_signing_txack(msg); -} - -void fsm_msgCipherKeyValue(CipherKeyValue *msg) -{ - CHECK_INITIALIZED - - CHECK_PARAM(msg->has_key, _("No key provided")); - CHECK_PARAM(msg->has_value, _("No value provided")); - CHECK_PARAM(msg->value.size % 16 == 0, _("Value length must be a multiple of 16")); - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - bool encrypt = msg->has_encrypt && msg->encrypt; - bool ask_on_encrypt = msg->has_ask_on_encrypt && msg->ask_on_encrypt; - bool ask_on_decrypt = msg->has_ask_on_decrypt && msg->ask_on_decrypt; - if ((encrypt && ask_on_encrypt) || (!encrypt && ask_on_decrypt)) { - layoutCipherKeyValue(encrypt, msg->key); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - uint8_t data[256 + 4]; - strlcpy((char *)data, msg->key, sizeof(data)); - strlcat((char *)data, ask_on_encrypt ? "E1" : "E0", sizeof(data)); - strlcat((char *)data, ask_on_decrypt ? "D1" : "D0", sizeof(data)); - - hmac_sha512(node->private_key, 32, data, strlen((char *)data), data); - - RESP_INIT(CipheredKeyValue); - if (encrypt) { - aes_encrypt_ctx ctx; - aes_encrypt_key256(data, &ctx); - aes_cbc_encrypt(msg->value.bytes, resp->value.bytes, msg->value.size, ((msg->iv.size == 16) ? (msg->iv.bytes) : (data + 32)), &ctx); - } else { - aes_decrypt_ctx ctx; - aes_decrypt_key256(data, &ctx); - aes_cbc_decrypt(msg->value.bytes, resp->value.bytes, msg->value.size, ((msg->iv.size == 16) ? (msg->iv.bytes) : (data + 32)), &ctx); - } - resp->has_value = true; - resp->value.size = msg->value.size; - msg_write(MessageType_MessageType_CipheredKeyValue, resp); - layoutHome(); -} - -void fsm_msgClearSession(ClearSession *msg) -{ - (void)msg; - session_clear(true); // clear PIN as well - layoutScreensaver(); - fsm_sendSuccess(_("Session cleared")); -} - -void fsm_msgApplySettings(ApplySettings *msg) -{ - CHECK_PARAM(msg->has_label || msg->has_language || msg->has_use_passphrase || msg->has_homescreen || msg->has_auto_lock_delay_ms, - _("No setting provided")); - - CHECK_PIN - - if (msg->has_label) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change name to"), msg->label, "?", NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - if (msg->has_language) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change language to"), msg->language, "?", NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - if (msg->has_use_passphrase) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), msg->use_passphrase ? _("enable passphrase") : _("disable passphrase"), _("encryption?"), NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - if (msg->has_homescreen) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change the home"), _("screen?"), NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - if (msg->has_auto_lock_delay_ms) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change auto-lock"), _("delay?"), NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - if (msg->has_label) { - storage_setLabel(msg->label); - } - if (msg->has_language) { - storage_setLanguage(msg->language); - } - if (msg->has_use_passphrase) { - storage_setPassphraseProtection(msg->use_passphrase); - } - if (msg->has_homescreen) { - storage_setHomescreen(msg->homescreen.bytes, msg->homescreen.size); - } - if (msg->has_auto_lock_delay_ms) { - storage_setAutoLockDelayMs(msg->auto_lock_delay_ms); - } - storage_update(); - fsm_sendSuccess(_("Settings applied")); - layoutHome(); -} - -void fsm_msgApplyFlags(ApplyFlags *msg) -{ - if (msg->has_flags) { - storage_applyFlags(msg->flags); - storage_update(); - } - fsm_sendSuccess(_("Flags applied")); -} - -static bool path_mismatched(const CoinInfo *coin, const GetAddress *msg) -{ - bool mismatch = false; - - // m : no path - if (msg->address_n_count == 0) { - return false; - } - - // m/44' : BIP44 Legacy - // m / purpose' / coin_type' / account' / change / address_index - if (msg->address_n[0] == (0x80000000 + 44)) { - mismatch |= (msg->script_type != InputScriptType_SPENDADDRESS); - mismatch |= (msg->address_n_count != 5); - mismatch |= (msg->address_n[1] != coin->coin_type); - mismatch |= (msg->address_n[2] & 0x80000000) == 0; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; - return mismatch; - } - - // m/45' - BIP45 Copay Abandoned Multisig P2SH - // m / purpose' / cosigner_index / change / address_index - if (msg->address_n[0] == (0x80000000 + 45)) { - mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG); - mismatch |= (msg->address_n_count != 4); - mismatch |= (msg->address_n[1] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[2] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - return mismatch; - } - - // m/48' - BIP48 Copay Multisig P2SH - // m / purpose' / coin_type' / account' / change / address_index - if (msg->address_n[0] == (0x80000000 + 48)) { - mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG); - mismatch |= (msg->address_n_count != 5); - mismatch |= (msg->address_n[1] != coin->coin_type); - mismatch |= (msg->address_n[2] & 0x80000000) == 0; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; - return mismatch; - } - - // m/49' : BIP49 SegWit - // m / purpose' / coin_type' / account' / change / address_index - if (msg->address_n[0] == (0x80000000 + 49)) { - mismatch |= (msg->script_type != InputScriptType_SPENDP2SHWITNESS); - mismatch |= !coin->has_segwit; - mismatch |= !coin->has_address_type_p2sh; - mismatch |= (msg->address_n_count != 5); - mismatch |= (msg->address_n[1] != coin->coin_type); - mismatch |= (msg->address_n[2] & 0x80000000) == 0; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; - return mismatch; - } - - // m/84' : BIP84 Native SegWit - // m / purpose' / coin_type' / account' / change / address_index - if (msg->address_n[0] == (0x80000000 + 84)) { - mismatch |= (msg->script_type != InputScriptType_SPENDWITNESS); - mismatch |= !coin->has_segwit; - mismatch |= !coin->bech32_prefix; - mismatch |= (msg->address_n_count != 5); - mismatch |= (msg->address_n[1] != coin->coin_type); - mismatch |= (msg->address_n[2] & 0x80000000) == 0; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; - return mismatch; - } - - return false; -} - -void fsm_msgGetAddress(GetAddress *msg) -{ - RESP_INIT(Address); - - CHECK_INITIALIZED - - CHECK_PIN - - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - hdnode_fill_public_key(node); - - char address[MAX_ADDR_SIZE]; - layoutProgress(_("Computing address"), 0); - if (!compute_address(coin, msg->script_type, node, msg->has_multisig, &msg->multisig, address)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Can't encode address")); - layoutHome(); - return; - } - - if (msg->has_show_display && msg->show_display) { - char desc[20]; - if (msg->has_multisig) { - strlcpy(desc, "Multisig __ of __:", sizeof(desc)); - const uint32_t m = msg->multisig.m; - const uint32_t n = msg->multisig.pubkeys_count; - desc[9] = (m < 10) ? ' ': ('0' + (m / 10)); - desc[10] = '0' + (m % 10); - desc[15] = (n < 10) ? ' ': ('0' + (n / 10)); - desc[16] = '0' + (n % 10); - } else { - strlcpy(desc, _("Address:"), sizeof(desc)); - } - - bool mismatch = path_mismatched(coin, msg); - - if (mismatch) { - layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL, _("Wrong address path"), _("for selected coin."), NULL, _("Continue at your"), _("own risk!"), NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - bool is_cashaddr = coin->cashaddr_prefix != NULL; - bool is_bech32 = msg->script_type == InputScriptType_SPENDWITNESS; - if (!fsm_layoutAddress(address, desc, is_cashaddr || is_bech32, is_cashaddr ? strlen(coin->cashaddr_prefix) + 1 : 0, msg->address_n, msg->address_n_count)) { - return; - } - } - - strlcpy(resp->address, address, sizeof(resp->address)); - msg_write(MessageType_MessageType_Address, resp); - layoutHome(); -} - -void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) -{ - RESP_INIT(EthereumAddress); - - CHECK_INITIALIZED - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - resp->address.size = 20; - - if (!hdnode_get_ethereum_pubkeyhash(node, resp->address.bytes)) - return; - - if (msg->has_show_display && msg->show_display) { - char desc[16]; - strlcpy(desc, "Address:", sizeof(desc)); - - char address[43] = { '0', 'x' }; - ethereum_address_checksum(resp->address.bytes, address + 2); - - if (!fsm_layoutAddress(address, desc, false, 0, msg->address_n, msg->address_n_count)) { - return; - } - } - - msg_write(MessageType_MessageType_EthereumAddress, resp); - layoutHome(); -} - -void fsm_msgEthereumSignMessage(EthereumSignMessage *msg) -{ - RESP_INIT(EthereumMessageSignature); - - CHECK_INITIALIZED - - layoutSignMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - ethereum_message_sign(msg, node, resp); - layoutHome(); -} - -void fsm_msgEthereumVerifyMessage(EthereumVerifyMessage *msg) -{ - CHECK_PARAM(msg->has_address, _("No address provided")); - CHECK_PARAM(msg->has_message, _("No message provided")); - - if (ethereum_message_verify(msg) != 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); - return; - } - - char address[43] = { '0', 'x' }; - ethereum_address_checksum(msg->address.bytes, address + 2); - layoutVerifyAddress(address); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - layoutVerifyMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - fsm_sendSuccess(_("Message verified")); - - layoutHome(); -} - -void fsm_msgEntropyAck(EntropyAck *msg) -{ - if (msg->has_entropy) { - reset_entropy(msg->entropy.bytes, msg->entropy.size); - } else { - reset_entropy(0, 0); - } -} - -void fsm_msgSignMessage(SignMessage *msg) -{ - RESP_INIT(MessageSignature); - - CHECK_INITIALIZED - - layoutSignMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - layoutProgressSwipe(_("Signing"), 0); - if (cryptoMessageSign(coin, node, msg->script_type, msg->message.bytes, msg->message.size, resp->signature.bytes) == 0) { - resp->has_address = true; - hdnode_fill_public_key(node); - if (!compute_address(coin, msg->script_type, node, false, NULL, resp->address)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error computing address")); - layoutHome(); - return; - } - resp->has_signature = true; - resp->signature.size = 65; - msg_write(MessageType_MessageType_MessageSignature, resp); - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error signing message")); - } - layoutHome(); -} - -void fsm_msgVerifyMessage(VerifyMessage *msg) -{ - CHECK_PARAM(msg->has_address, _("No address provided")); - CHECK_PARAM(msg->has_message, _("No message provided")); - - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - layoutProgressSwipe(_("Verifying"), 0); - if (msg->signature.size == 65 && cryptoMessageVerify(coin, msg->message.bytes, msg->message.size, msg->address, msg->signature.bytes) == 0) { - layoutVerifyAddress(msg->address); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - layoutVerifyMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - fsm_sendSuccess(_("Message verified")); - } else { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); - } - layoutHome(); -} - -void fsm_msgSignIdentity(SignIdentity *msg) -{ - RESP_INIT(SignedIdentity); - - CHECK_INITIALIZED - - layoutSignIdentity(&(msg->identity), msg->has_challenge_visual ? msg->challenge_visual : 0); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - uint8_t hash[32]; - if (!msg->has_identity || cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); - layoutHome(); - return; - } - - uint32_t address_n[5]; - address_n[0] = 0x80000000 | 13; - address_n[1] = 0x80000000 | hash[ 0] | (hash[ 1] << 8) | (hash[ 2] << 16) | ((uint32_t) hash[ 3] << 24); - address_n[2] = 0x80000000 | hash[ 4] | (hash[ 5] << 8) | (hash[ 6] << 16) | ((uint32_t) hash[ 7] << 24); - address_n[3] = 0x80000000 | hash[ 8] | (hash[ 9] << 8) | (hash[10] << 16) | ((uint32_t) hash[11] << 24); - address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | ((uint32_t) hash[15] << 24); - - const char *curve = SECP256K1_NAME; - if (msg->has_ecdsa_curve_name) { - curve = msg->ecdsa_curve_name; - } - HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL); - if (!node) return; - - bool sign_ssh = msg->identity.has_proto && (strcmp(msg->identity.proto, "ssh") == 0); - bool sign_gpg = msg->identity.has_proto && (strcmp(msg->identity.proto, "gpg") == 0); - - int result = 0; - layoutProgressSwipe(_("Signing"), 0); - if (sign_ssh) { // SSH does not sign visual challenge - result = sshMessageSign(node, msg->challenge_hidden.bytes, msg->challenge_hidden.size, resp->signature.bytes); - } else if (sign_gpg) { // GPG should sign a message digest - result = gpgMessageSign(node, msg->challenge_hidden.bytes, msg->challenge_hidden.size, resp->signature.bytes); - } else { - uint8_t digest[64]; - sha256_Raw(msg->challenge_hidden.bytes, msg->challenge_hidden.size, digest); - sha256_Raw((const uint8_t *)msg->challenge_visual, strlen(msg->challenge_visual), digest + 32); - result = cryptoMessageSign(&(coins[0]), node, InputScriptType_SPENDADDRESS, digest, 64, resp->signature.bytes); - } - - if (result == 0) { - hdnode_fill_public_key(node); - if (strcmp(curve, SECP256K1_NAME) != 0) { - resp->has_address = false; - } else { - resp->has_address = true; - hdnode_get_address(node, 0x00, resp->address, sizeof(resp->address)); // hardcoded Bitcoin address type - } - resp->has_public_key = true; - resp->public_key.size = 33; - memcpy(resp->public_key.bytes, node->public_key, 33); - if (node->public_key[0] == 1) { - /* ed25519 public key */ - resp->public_key.bytes[0] = 0; - } - resp->has_signature = true; - resp->signature.size = 65; - msg_write(MessageType_MessageType_SignedIdentity, resp); - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error signing identity")); - } - layoutHome(); -} - -void fsm_msgGetECDHSessionKey(GetECDHSessionKey *msg) -{ - RESP_INIT(ECDHSessionKey); - - CHECK_INITIALIZED - - layoutDecryptIdentity(&msg->identity); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - uint8_t hash[32]; - if (!msg->has_identity || cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); - layoutHome(); - return; - } - - uint32_t address_n[5]; - address_n[0] = 0x80000000 | 17; - address_n[1] = 0x80000000 | hash[ 0] | (hash[ 1] << 8) | (hash[ 2] << 16) | ((uint32_t) hash[ 3] << 24); - address_n[2] = 0x80000000 | hash[ 4] | (hash[ 5] << 8) | (hash[ 6] << 16) | ((uint32_t) hash[ 7] << 24); - address_n[3] = 0x80000000 | hash[ 8] | (hash[ 9] << 8) | (hash[10] << 16) | ((uint32_t) hash[11] << 24); - address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | ((uint32_t) hash[15] << 24); - - const char *curve = SECP256K1_NAME; - if (msg->has_ecdsa_curve_name) { - curve = msg->ecdsa_curve_name; - } - - const HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL); - if (!node) return; - - int result_size = 0; - if (hdnode_get_shared_key(node, msg->peer_public_key.bytes, resp->session_key.bytes, &result_size) == 0) { - resp->has_session_key = true; - resp->session_key.size = result_size; - msg_write(MessageType_MessageType_ECDHSessionKey, resp); - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error getting ECDH session key")); - } - layoutHome(); -} - -/* ECIES disabled -void fsm_msgEncryptMessage(EncryptMessage *msg) -{ - CHECK_INITIALIZED - - CHECK_PARAM(msg->has_pubkey, _("No public key provided")); - CHECK_PARAM(msg->has_message, _("No message provided")); - CHECK_PARAM(msg->pubkey.size == 33, _("Invalid public key provided")); - curve_point pubkey; - CHECK_PARAM(ecdsa_read_pubkey(&secp256k1, msg->pubkey.bytes, &pubkey) == 1, _("Invalid public key provided")); - - bool display_only = msg->has_display_only && msg->display_only; - bool signing = msg->address_n_count > 0; - RESP_INIT(EncryptedMessage); - const HDNode *node = 0; - uint8_t address_raw[MAX_ADDR_RAW_SIZE]; - if (signing) { - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - - CHECK_PIN - - node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - hdnode_get_address_raw(node, coin->address_type, address_raw); - } - layoutEncryptMessage(msg->message.bytes, msg->message.size, signing); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - layoutProgressSwipe(_("Encrypting"), 0); - if (cryptoMessageEncrypt(&pubkey, msg->message.bytes, msg->message.size, display_only, resp->nonce.bytes, &(resp->nonce.size), resp->message.bytes, &(resp->message.size), resp->hmac.bytes, &(resp->hmac.size), signing ? node->private_key : 0, signing ? address_raw : 0) != 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error encrypting message")); - layoutHome(); - return; - } - resp->has_nonce = true; - resp->has_message = true; - resp->has_hmac = true; - msg_write(MessageType_MessageType_EncryptedMessage, resp); - layoutHome(); -} - -void fsm_msgDecryptMessage(DecryptMessage *msg) -{ - CHECK_INITIALIZED - - CHECK_PARAM(msg->has_nonce, _("No nonce provided")); - CHECK_PARAM(msg->has_message, _("No message provided")); - CHECK_PARAM(msg->has_hmac, _("No message hmac provided")); - - CHECK_PARAM(msg->nonce.size == 33, _("Invalid nonce key provided")); - curve_point nonce_pubkey; - CHECK_PARAM(ecdsa_read_pubkey(&secp256k1, msg->nonce.bytes, &nonce_pubkey) == 1, _("Invalid nonce provided")); - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - layoutProgressSwipe(_("Decrypting"), 0); - RESP_INIT(DecryptedMessage); - bool display_only = false; - bool signing = false; - uint8_t address_raw[MAX_ADDR_RAW_SIZE]; - if (cryptoMessageDecrypt(&nonce_pubkey, msg->message.bytes, msg->message.size, msg->hmac.bytes, msg->hmac.size, node->private_key, resp->message.bytes, &(resp->message.size), &display_only, &signing, address_raw) != 0) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - if (signing) { - base58_encode_check(address_raw, 21, resp->address, sizeof(resp->address)); - } - layoutDecryptMessage(resp->message.bytes, resp->message.size, signing ? resp->address : 0); - protectButton(ButtonRequestType_ButtonRequest_Other, true); - if (display_only) { - resp->has_address = false; - resp->has_message = false; - memset(resp->address, 0, sizeof(resp->address)); - memset(&(resp->message), 0, sizeof(resp->message)); - } else { - resp->has_address = signing; - resp->has_message = true; - } - msg_write(MessageType_MessageType_DecryptedMessage, resp); - layoutHome(); -} -*/ - -void fsm_msgRecoveryDevice(RecoveryDevice *msg) -{ - const bool dry_run = msg->has_dry_run ? msg->dry_run : false; - if (dry_run) { - CHECK_PIN - } else { - CHECK_NOT_INITIALIZED - } - - CHECK_PARAM(!msg->has_word_count || msg->word_count == 12 || msg->word_count == 18 || msg->word_count == 24, _("Invalid word count")); - - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("recover the device?"), NULL, NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - recovery_init( - msg->has_word_count ? msg->word_count : 12, - msg->has_passphrase_protection && msg->passphrase_protection, - msg->has_pin_protection && msg->pin_protection, - msg->has_language ? msg->language : 0, - msg->has_label ? msg->label : 0, - msg->has_enforce_wordlist && msg->enforce_wordlist, - msg->has_type ? msg->type : 0, - msg->has_u2f_counter ? msg->u2f_counter : 0, - dry_run - ); -} - -void fsm_msgWordAck(WordAck *msg) -{ - recovery_word(msg->word); -} - -void fsm_msgSetU2FCounter(SetU2FCounter *msg) -{ - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you want to set"), _("the U2F counter?"), NULL, NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - storage_setU2FCounter(msg->u2f_counter); - storage_update(); - fsm_sendSuccess(_("U2F counter set")); - layoutHome(); -} - -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); - - 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)) - 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)) { - 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) - - CHECK_PARAM(msg->has_transaction, _("No common provided")); - - // 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); - - HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n, msg->transaction.address_n_count, NULL); - if (!node) return; - - hdnode_fill_public_key(node); - - 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) { - msg->transfer.mosaics_count = nem_canonicalizeMosaics(msg->transfer.mosaics, msg->transfer.mosaics_count); - } - - 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->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(); -} - -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")); - - 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; - } - - CHECK_PIN - - 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->has_payload = true; - 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(); -} - -void fsm_msgCosiCommit(CosiCommit *msg) -{ - RESP_INIT(CosiCommitment); - - CHECK_INITIALIZED - - CHECK_PARAM(msg->has_data, _("No data provided")); - - layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, msg->data.size, false); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - uint8_t nonce[32]; - sha256_Raw(msg->data.bytes, msg->data.size, nonce); - rfc6979_state rng; - init_rfc6979(node->private_key, nonce, &rng); - generate_rfc6979(nonce, &rng); - - resp->has_commitment = true; - resp->has_pubkey = true; - resp->commitment.size = 32; - resp->pubkey.size = 32; - - ed25519_publickey(nonce, resp->commitment.bytes); - ed25519_publickey(node->private_key, resp->pubkey.bytes); - - msg_write(MessageType_MessageType_CosiCommitment, resp); - layoutHome(); -} - -void fsm_msgCosiSign(CosiSign *msg) -{ - RESP_INIT(CosiSignature); - - CHECK_INITIALIZED - - CHECK_PARAM(msg->has_data, _("No data provided")); - CHECK_PARAM(msg->has_global_commitment && msg->global_commitment.size == 32, _("Invalid global commitment")); - CHECK_PARAM(msg->has_global_pubkey && msg->global_pubkey.size == 32, _("Invalid global pubkey")); - - layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, msg->data.size, true); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - uint8_t nonce[32]; - sha256_Raw(msg->data.bytes, msg->data.size, nonce); - rfc6979_state rng; - init_rfc6979(node->private_key, nonce, &rng); - generate_rfc6979(nonce, &rng); - - resp->has_signature = true; - resp->signature.size = 32; - - ed25519_cosi_sign(msg->data.bytes, msg->data.size, node->private_key, nonce, msg->global_commitment.bytes, msg->global_pubkey.bytes, resp->signature.bytes); - - msg_write(MessageType_MessageType_CosiSignature, resp); - layoutHome(); -} - -void fsm_msgStellarGetPublicKey(StellarGetPublicKey *msg) -{ - RESP_INIT(StellarPublicKey); - - CHECK_INITIALIZED - - CHECK_PIN - - // Will exit if the user does not confirm - stellar_layoutGetPublicKey(msg->address_n, msg->address_n_count); - - // Read public key and write it to the response - resp->has_public_key = true; - resp->public_key.size = 32; - stellar_getPubkeyAtAddress(msg->address_n, msg->address_n_count, resp->public_key.bytes, sizeof(resp->public_key.bytes)); - - msg_write(MessageType_MessageType_StellarPublicKey, resp); - - layoutHome(); -} - -void fsm_msgStellarSignMessage(StellarSignMessage *msg) -{ - CHECK_INITIALIZED - CHECK_PIN - - RESP_INIT(StellarMessageSignature); - - // Will exit if the user does not confirm - stellar_confirmSignString(msg, resp); - - msg_write(MessageType_MessageType_StellarMessageSignature, resp); - - layoutHome(); -} - -void fsm_msgStellarVerifyMessage(StellarVerifyMessage *msg) -{ - if (!stellar_verifySignature(msg)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); - return; - } - - fsm_sendSuccess(_("Message verified")); - layoutHome(); -} - -void fsm_msgStellarSignTx(StellarSignTx *msg) -{ - CHECK_INITIALIZED - CHECK_PIN - - stellar_signingInit(msg); - - // Confirm transaction basics - stellar_layoutTransactionSummary(msg); - - // Respond with a request for the first operation - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); -} - -void fsm_msgStellarCreateAccountOp(StellarCreateAccountOp *msg) -{ - stellar_confirmCreateAccountOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarPaymentOp(StellarPaymentOp *msg) -{ - // This will display additional dialogs to the user - stellar_confirmPaymentOp(msg); - - // Last operation was confirmed, send a StellarSignedTx - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarPathPaymentOp(StellarPathPaymentOp *msg) -{ - stellar_confirmPathPaymentOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarManageOfferOp(StellarManageOfferOp *msg) -{ - stellar_confirmManageOfferOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarCreatePassiveOfferOp(StellarCreatePassiveOfferOp *msg) -{ - stellar_confirmCreatePassiveOfferOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarSetOptionsOp(StellarSetOptionsOp *msg) -{ - stellar_confirmSetOptionsOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarChangeTrustOp(StellarChangeTrustOp *msg) -{ - stellar_confirmChangeTrustOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarAllowTrustOp(StellarAllowTrustOp *msg) -{ - stellar_confirmAllowTrustOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarAccountMergeOp(StellarAccountMergeOp *msg) -{ - stellar_confirmAccountMergeOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarManageDataOp(StellarManageDataOp *msg) -{ - stellar_confirmManageDataOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -void fsm_msgStellarBumpSequenceOp(StellarBumpSequenceOp *msg) -{ - stellar_confirmBumpSequenceOp(msg); - - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } -} - -#if DEBUG_LINK - -void fsm_msgDebugLinkGetState(DebugLinkGetState *msg) -{ - (void)msg; - - // Do not use RESP_INIT because it clears msg_resp, but another message - // might be being handled - DebugLinkState resp; - memset(&resp, 0, sizeof(resp)); - - resp.has_layout = true; - resp.layout.size = OLED_BUFSIZE; - memcpy(resp.layout.bytes, oledGetBuffer(), OLED_BUFSIZE); - - if (storage_hasPin()) { - resp.has_pin = true; - strlcpy(resp.pin, storage_getPin(), sizeof(resp.pin)); - } - - resp.has_matrix = true; - strlcpy(resp.matrix, pinmatrix_get(), sizeof(resp.matrix)); - - resp.has_reset_entropy = true; - resp.reset_entropy.size = reset_get_int_entropy(resp.reset_entropy.bytes); - - resp.has_reset_word = true; - strlcpy(resp.reset_word, reset_get_word(), sizeof(resp.reset_word)); - - resp.has_recovery_fake_word = true; - strlcpy(resp.recovery_fake_word, recovery_get_fake_word(), sizeof(resp.recovery_fake_word)); - - resp.has_recovery_word_pos = true; - resp.recovery_word_pos = recovery_get_word_pos(); - - if (storage_hasMnemonic()) { - resp.has_mnemonic = true; - strlcpy(resp.mnemonic, storage_getMnemonic(), sizeof(resp.mnemonic)); - } - - if (storage_hasNode()) { - resp.has_node = true; - storage_dumpNode(&(resp.node)); - } - - resp.has_passphrase_protection = true; - resp.passphrase_protection = storage_hasPassphraseProtection(); - - msg_debug_write(MessageType_MessageType_DebugLinkState, &resp); -} - -void fsm_msgDebugLinkStop(DebugLinkStop *msg) -{ - (void)msg; -} - -void fsm_msgDebugLinkMemoryRead(DebugLinkMemoryRead *msg) -{ - RESP_INIT(DebugLinkMemory); - - uint32_t length = 1024; - if (msg->has_length && msg->length < length) - length = msg->length; - resp->has_memory = true; - memcpy(resp->memory.bytes, FLASH_PTR(msg->address), length); - resp->memory.size = length; - msg_debug_write(MessageType_MessageType_DebugLinkMemory, resp); -} - -void fsm_msgDebugLinkMemoryWrite(DebugLinkMemoryWrite *msg) -{ - uint32_t length = msg->memory.size; - if (msg->flash) { - svc_flash_unlock(); - svc_flash_program(FLASH_CR_PROGRAM_X32); - for (uint32_t i = 0; i < length; i += 4) { - uint32_t word; - memcpy(&word, msg->memory.bytes + i, 4); - flash_write32(msg->address + i, word); - } - uint32_t dummy = svc_flash_lock(); - (void)dummy; - } else { -#if !EMULATOR - memcpy((void *) msg->address, msg->memory.bytes, length); -#endif - } -} - -void fsm_msgDebugLinkFlashErase(DebugLinkFlashErase *msg) -{ - svc_flash_unlock(); - svc_flash_erase_sector(msg->sector); - uint32_t dummy = svc_flash_lock(); - (void)dummy; -} -#endif +#include "fsm_msg_common.h" +#include "fsm_msg_coin.h" +#include "fsm_msg_ethereum.h" +#include "fsm_msg_crypto.h" +#include "fsm_msg_nem.h" +#include "fsm_msg_stellar.h" +#include "fsm_msg_debug.h" diff --git a/firmware/fsm_msg_coin.h b/firmware/fsm_msg_coin.h new file mode 100644 index 0000000000..c482fe8f65 --- /dev/null +++ b/firmware/fsm_msg_coin.h @@ -0,0 +1,272 @@ +void fsm_msgGetPublicKey(GetPublicKey *msg) +{ + RESP_INIT(PublicKey); + + CHECK_INITIALIZED + + CHECK_PIN + + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + + const char *curve = coin->curve_name; + if (msg->has_ecdsa_curve_name) { + curve = msg->ecdsa_curve_name; + } + uint32_t fingerprint; + HDNode *node = node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count, &fingerprint); + if (!node) return; + hdnode_fill_public_key(node); + + if (msg->has_show_display && msg->show_display) { + layoutPublicKey(node->public_key); + if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + resp->node.depth = node->depth; + resp->node.fingerprint = fingerprint; + resp->node.child_num = node->child_num; + resp->node.chain_code.size = 32; + memcpy(resp->node.chain_code.bytes, node->chain_code, 32); + resp->node.has_private_key = false; + resp->node.has_public_key = true; + resp->node.public_key.size = 33; + memcpy(resp->node.public_key.bytes, node->public_key, 33); + if (node->public_key[0] == 1) { + /* ed25519 public key */ + resp->node.public_key.bytes[0] = 0; + } + resp->has_xpub = true; + hdnode_serialize_public(node, fingerprint, coin->xpub_magic, resp->xpub, sizeof(resp->xpub)); + msg_write(MessageType_MessageType_PublicKey, resp); + layoutHome(); +} + +void fsm_msgSignTx(SignTx *msg) +{ + CHECK_INITIALIZED + + CHECK_PARAM(msg->inputs_count > 0, _("Transaction must have at least one input")); + CHECK_PARAM(msg->outputs_count > 0, _("Transaction must have at least one output")); + CHECK_PARAM(msg->inputs_count + msg->outputs_count >= msg->inputs_count, _("Value overflow")); + + CHECK_PIN + + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + const HDNode *node = fsm_getDerivedNode(coin->curve_name, NULL, 0, NULL); + if (!node) return; + + signing_init(msg, coin, node); +} + +void fsm_msgTxAck(TxAck *msg) +{ + CHECK_PARAM(msg->has_tx, _("No transaction provided")); + + signing_txack(&(msg->tx)); +} + +static bool path_mismatched(const CoinInfo *coin, const GetAddress *msg) +{ + bool mismatch = false; + + // m : no path + if (msg->address_n_count == 0) { + return false; + } + + // m/44' : BIP44 Legacy + // m / purpose' / coin_type' / account' / change / address_index + if (msg->address_n[0] == (0x80000000 + 44)) { + mismatch |= (msg->script_type != InputScriptType_SPENDADDRESS); + mismatch |= (msg->address_n_count != 5); + mismatch |= (msg->address_n[1] != coin->coin_type); + mismatch |= (msg->address_n[2] & 0x80000000) == 0; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; + return mismatch; + } + + // m/45' - BIP45 Copay Abandoned Multisig P2SH + // m / purpose' / cosigner_index / change / address_index + if (msg->address_n[0] == (0x80000000 + 45)) { + mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG); + mismatch |= (msg->address_n_count != 4); + mismatch |= (msg->address_n[1] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[2] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + return mismatch; + } + + // m/48' - BIP48 Copay Multisig P2SH + // m / purpose' / coin_type' / account' / change / address_index + if (msg->address_n[0] == (0x80000000 + 48)) { + mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG); + mismatch |= (msg->address_n_count != 5); + mismatch |= (msg->address_n[1] != coin->coin_type); + mismatch |= (msg->address_n[2] & 0x80000000) == 0; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; + return mismatch; + } + + // m/49' : BIP49 SegWit + // m / purpose' / coin_type' / account' / change / address_index + if (msg->address_n[0] == (0x80000000 + 49)) { + mismatch |= (msg->script_type != InputScriptType_SPENDP2SHWITNESS); + mismatch |= !coin->has_segwit; + mismatch |= !coin->has_address_type_p2sh; + mismatch |= (msg->address_n_count != 5); + mismatch |= (msg->address_n[1] != coin->coin_type); + mismatch |= (msg->address_n[2] & 0x80000000) == 0; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; + return mismatch; + } + + // m/84' : BIP84 Native SegWit + // m / purpose' / coin_type' / account' / change / address_index + if (msg->address_n[0] == (0x80000000 + 84)) { + mismatch |= (msg->script_type != InputScriptType_SPENDWITNESS); + mismatch |= !coin->has_segwit; + mismatch |= !coin->bech32_prefix; + mismatch |= (msg->address_n_count != 5); + mismatch |= (msg->address_n[1] != coin->coin_type); + mismatch |= (msg->address_n[2] & 0x80000000) == 0; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; + return mismatch; + } + + return false; +} + +void fsm_msgGetAddress(GetAddress *msg) +{ + RESP_INIT(Address); + + CHECK_INITIALIZED + + CHECK_PIN + + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + hdnode_fill_public_key(node); + + char address[MAX_ADDR_SIZE]; + layoutProgress(_("Computing address"), 0); + if (!compute_address(coin, msg->script_type, node, msg->has_multisig, &msg->multisig, address)) { + fsm_sendFailure(FailureType_Failure_DataError, _("Can't encode address")); + layoutHome(); + return; + } + + if (msg->has_show_display && msg->show_display) { + char desc[20]; + if (msg->has_multisig) { + strlcpy(desc, "Multisig __ of __:", sizeof(desc)); + const uint32_t m = msg->multisig.m; + const uint32_t n = msg->multisig.pubkeys_count; + desc[9] = (m < 10) ? ' ': ('0' + (m / 10)); + desc[10] = '0' + (m % 10); + desc[15] = (n < 10) ? ' ': ('0' + (n / 10)); + desc[16] = '0' + (n % 10); + } else { + strlcpy(desc, _("Address:"), sizeof(desc)); + } + + bool mismatch = path_mismatched(coin, msg); + + if (mismatch) { + layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL, _("Wrong address path"), _("for selected coin."), NULL, _("Continue at your"), _("own risk!"), NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + bool is_cashaddr = coin->cashaddr_prefix != NULL; + bool is_bech32 = msg->script_type == InputScriptType_SPENDWITNESS; + if (!fsm_layoutAddress(address, desc, is_cashaddr || is_bech32, is_cashaddr ? strlen(coin->cashaddr_prefix) + 1 : 0, msg->address_n, msg->address_n_count)) { + return; + } + } + + strlcpy(resp->address, address, sizeof(resp->address)); + msg_write(MessageType_MessageType_Address, resp); + layoutHome(); +} + +void fsm_msgSignMessage(SignMessage *msg) +{ + RESP_INIT(MessageSignature); + + CHECK_INITIALIZED + + layoutSignMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + + layoutProgressSwipe(_("Signing"), 0); + if (cryptoMessageSign(coin, node, msg->script_type, msg->message.bytes, msg->message.size, resp->signature.bytes) == 0) { + resp->has_address = true; + hdnode_fill_public_key(node); + if (!compute_address(coin, msg->script_type, node, false, NULL, resp->address)) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Error computing address")); + layoutHome(); + return; + } + resp->has_signature = true; + resp->signature.size = 65; + msg_write(MessageType_MessageType_MessageSignature, resp); + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Error signing message")); + } + layoutHome(); +} + +void fsm_msgVerifyMessage(VerifyMessage *msg) +{ + CHECK_PARAM(msg->has_address, _("No address provided")); + CHECK_PARAM(msg->has_message, _("No message provided")); + + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + layoutProgressSwipe(_("Verifying"), 0); + if (msg->signature.size == 65 && cryptoMessageVerify(coin, msg->message.bytes, msg->message.size, msg->address, msg->signature.bytes) == 0) { + layoutVerifyAddress(msg->address); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + layoutVerifyMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + fsm_sendSuccess(_("Message verified")); + } else { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); + } + layoutHome(); +} diff --git a/firmware/fsm_msg_common.h b/firmware/fsm_msg_common.h new file mode 100644 index 0000000000..0cee17d56e --- /dev/null +++ b/firmware/fsm_msg_common.h @@ -0,0 +1,399 @@ +void fsm_msgInitialize(Initialize *msg) +{ + recovery_abort(); + signing_abort(); + if (msg && msg->has_state && msg->state.size == 64) { + uint8_t i_state[64]; + if (!session_getState(msg->state.bytes, i_state, NULL)) { + session_clear(false); // do not clear PIN + } else { + if (0 != memcmp(msg->state.bytes, i_state, 64)) { + session_clear(false); // do not clear PIN + } + } + } else { + session_clear(false); // do not clear PIN + } + layoutHome(); + fsm_msgGetFeatures(0); +} + +void fsm_msgGetFeatures(GetFeatures *msg) +{ + (void)msg; + RESP_INIT(Features); + resp->has_vendor = true; strlcpy(resp->vendor, "bitcointrezor.com", sizeof(resp->vendor)); + resp->has_major_version = true; resp->major_version = VERSION_MAJOR; + resp->has_minor_version = true; resp->minor_version = VERSION_MINOR; + resp->has_patch_version = true; resp->patch_version = VERSION_PATCH; + resp->has_device_id = true; strlcpy(resp->device_id, storage_uuid_str, sizeof(resp->device_id)); + resp->has_pin_protection = true; resp->pin_protection = storage_hasPin(); + resp->has_passphrase_protection = true; resp->passphrase_protection = storage_hasPassphraseProtection(); +#ifdef SCM_REVISION + int len = sizeof(SCM_REVISION) - 1; + resp->has_revision = true; memcpy(resp->revision.bytes, SCM_REVISION, len); resp->revision.size = len; +#endif + resp->has_bootloader_hash = true; resp->bootloader_hash.size = memory_bootloader_hash(resp->bootloader_hash.bytes); + if (storage_getLanguage()) { + resp->has_language = true; + strlcpy(resp->language, storage_getLanguage(), sizeof(resp->language)); + } + if (storage_getLabel()) { + resp->has_label = true; + strlcpy(resp->label, storage_getLabel(), sizeof(resp->label)); + } + + _Static_assert(pb_arraysize(Features, coins) >= COINS_COUNT, "Features.coins max_count not large enough"); + + resp->coins_count = COINS_COUNT; + for (int i = 0; i < COINS_COUNT; i++) { + if (coins[i].coin_name) { + resp->coins[i].has_coin_name = true; + strlcpy(resp->coins[i].coin_name, coins[i].coin_name, sizeof(resp->coins[i].coin_name)); + } + if (coins[i].coin_shortcut) { + resp->coins[i].has_coin_shortcut = true; + strlcpy(resp->coins[i].coin_shortcut, coins[i].coin_shortcut + 1, sizeof(resp->coins[i].coin_shortcut)); + } + resp->coins[i].has_address_type = coins[i].has_address_type; + resp->coins[i].address_type = coins[i].address_type; + resp->coins[i].has_maxfee_kb = true; + resp->coins[i].maxfee_kb = coins[i].maxfee_kb; + resp->coins[i].has_address_type_p2sh = coins[i].has_address_type_p2sh; + resp->coins[i].address_type_p2sh = coins[i].address_type_p2sh; + resp->coins[i].has_xpub_magic = coins[i].xpub_magic != 0; + resp->coins[i].xpub_magic = coins[i].xpub_magic; + resp->coins[i].has_xprv_magic = coins[i].xprv_magic != 0; + resp->coins[i].xprv_magic = coins[i].xprv_magic; + resp->coins[i].has_segwit = true; + resp->coins[i].segwit = coins[i].has_segwit; + resp->coins[i].has_forkid = coins[i].has_forkid; + resp->coins[i].forkid = coins[i].forkid; + resp->coins[i].has_force_bip143 = true; + resp->coins[i].force_bip143 = coins[i].force_bip143; + } + resp->has_initialized = true; resp->initialized = storage_isInitialized(); + resp->has_imported = true; resp->imported = storage_isImported(); + resp->has_pin_cached = true; resp->pin_cached = session_isPinCached(); + resp->has_passphrase_cached = true; resp->passphrase_cached = session_isPassphraseCached(); + resp->has_needs_backup = true; resp->needs_backup = storage_needsBackup(); + resp->unfinished_backup = true; resp->unfinished_backup = storage_unfinishedBackup(); + resp->has_flags = true; resp->flags = storage_getFlags(); + resp->has_model = true; strlcpy(resp->model, "1", sizeof(resp->model)); + + msg_write(MessageType_MessageType_Features, resp); +} + +void fsm_msgPing(Ping *msg) +{ + RESP_INIT(Success); + + if (msg->has_button_protection && msg->button_protection) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("answer to ping?"), NULL, NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + if (msg->has_pin_protection && msg->pin_protection) { + CHECK_PIN + } + + if (msg->has_passphrase_protection && msg->passphrase_protection) { + if (!protectPassphrase()) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + return; + } + } + + if (msg->has_message) { + resp->has_message = true; + memcpy(&(resp->message), &(msg->message), sizeof(resp->message)); + } + msg_write(MessageType_MessageType_Success, resp); + layoutHome(); +} + +void fsm_msgChangePin(ChangePin *msg) +{ + bool removal = msg->has_remove && msg->remove; + if (removal) { + if (storage_hasPin()) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("remove current PIN?"), NULL, NULL, NULL, NULL); + } else { + fsm_sendSuccess(_("PIN removed")); + return; + } + } else { + if (storage_hasPin()) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change current PIN?"), NULL, NULL, NULL, NULL); + } else { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("set new PIN?"), NULL, NULL, NULL, NULL); + } + } + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN_UNCACHED + + if (removal) { + storage_setPin(""); + storage_update(); + fsm_sendSuccess(_("PIN removed")); + } else { + if (protectChangePin()) { + fsm_sendSuccess(_("PIN changed")); + } else { + fsm_sendFailure(FailureType_Failure_PinMismatch, NULL); + } + } + layoutHome(); +} + +void fsm_msgWipeDevice(WipeDevice *msg) +{ + (void)msg; + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("wipe the device?"), NULL, _("All data will be lost."), NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_WipeDevice, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + storage_wipe(); + // the following does not work on Mac anyway :-/ Linux/Windows are fine, so it is not needed + // usbReconnect(); // force re-enumeration because of the serial number change + fsm_sendSuccess(_("Device wiped")); + layoutHome(); +} + +void fsm_msgGetEntropy(GetEntropy *msg) +{ +#if !DEBUG_RNG + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("send entropy?"), NULL, NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } +#endif + RESP_INIT(Entropy); + uint32_t len = msg->size; + if (len > 1024) { + len = 1024; + } + resp->entropy.size = len; + random_buffer(resp->entropy.bytes, len); + msg_write(MessageType_MessageType_Entropy, resp); + layoutHome(); +} + +void fsm_msgLoadDevice(LoadDevice *msg) +{ + CHECK_NOT_INITIALIZED + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("I take the risk"), NULL, _("Loading private seed"), _("is not recommended."), _("Continue only if you"), _("know what you are"), _("doing!"), NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (msg->has_mnemonic && !(msg->has_skip_checksum && msg->skip_checksum) ) { + if (!mnemonic_check(msg->mnemonic)) { + fsm_sendFailure(FailureType_Failure_DataError, _("Mnemonic with wrong checksum provided")); + layoutHome(); + return; + } + } + + storage_loadDevice(msg); + fsm_sendSuccess(_("Device loaded")); + layoutHome(); +} + +void fsm_msgResetDevice(ResetDevice *msg) +{ + CHECK_NOT_INITIALIZED + + CHECK_PARAM(!msg->has_strength || msg->strength == 128 || msg->strength == 192 || msg->strength == 256, _("Invalid seed strength")); + + reset_init( + msg->has_display_random && msg->display_random, + msg->has_strength ? msg->strength : 128, + msg->has_passphrase_protection && msg->passphrase_protection, + msg->has_pin_protection && msg->pin_protection, + msg->has_language ? msg->language : 0, + msg->has_label ? msg->label : 0, + msg->has_u2f_counter ? msg->u2f_counter : 0, + msg->has_skip_backup ? msg->skip_backup : false + ); +} + +void fsm_msgEntropyAck(EntropyAck *msg) +{ + if (msg->has_entropy) { + reset_entropy(msg->entropy.bytes, msg->entropy.size); + } else { + reset_entropy(0, 0); + } +} + +void fsm_msgBackupDevice(BackupDevice *msg) +{ + CHECK_INITIALIZED + + CHECK_PIN_UNCACHED + + (void)msg; + reset_backup(true); +} + +void fsm_msgCancel(Cancel *msg) +{ + (void)msg; + recovery_abort(); + signing_abort(); + ethereum_signing_abort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); +} + +void fsm_msgClearSession(ClearSession *msg) +{ + (void)msg; + session_clear(true); // clear PIN as well + layoutScreensaver(); + fsm_sendSuccess(_("Session cleared")); +} + +void fsm_msgApplySettings(ApplySettings *msg) +{ + CHECK_PARAM(msg->has_label || msg->has_language || msg->has_use_passphrase || msg->has_homescreen || msg->has_auto_lock_delay_ms, + _("No setting provided")); + + CHECK_PIN + + if (msg->has_label) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change name to"), msg->label, "?", NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + if (msg->has_language) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change language to"), msg->language, "?", NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + if (msg->has_use_passphrase) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), msg->use_passphrase ? _("enable passphrase") : _("disable passphrase"), _("encryption?"), NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + if (msg->has_homescreen) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change the home"), _("screen?"), NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + if (msg->has_auto_lock_delay_ms) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change auto-lock"), _("delay?"), NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + if (msg->has_label) { + storage_setLabel(msg->label); + } + if (msg->has_language) { + storage_setLanguage(msg->language); + } + if (msg->has_use_passphrase) { + storage_setPassphraseProtection(msg->use_passphrase); + } + if (msg->has_homescreen) { + storage_setHomescreen(msg->homescreen.bytes, msg->homescreen.size); + } + if (msg->has_auto_lock_delay_ms) { + storage_setAutoLockDelayMs(msg->auto_lock_delay_ms); + } + storage_update(); + fsm_sendSuccess(_("Settings applied")); + layoutHome(); +} + +void fsm_msgApplyFlags(ApplyFlags *msg) +{ + if (msg->has_flags) { + storage_applyFlags(msg->flags); + storage_update(); + } + fsm_sendSuccess(_("Flags applied")); +} + +void fsm_msgRecoveryDevice(RecoveryDevice *msg) +{ + const bool dry_run = msg->has_dry_run ? msg->dry_run : false; + if (dry_run) { + CHECK_PIN + } else { + CHECK_NOT_INITIALIZED + } + + CHECK_PARAM(!msg->has_word_count || msg->word_count == 12 || msg->word_count == 18 || msg->word_count == 24, _("Invalid word count")); + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("recover the device?"), NULL, NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + recovery_init( + msg->has_word_count ? msg->word_count : 12, + msg->has_passphrase_protection && msg->passphrase_protection, + msg->has_pin_protection && msg->pin_protection, + msg->has_language ? msg->language : 0, + msg->has_label ? msg->label : 0, + msg->has_enforce_wordlist && msg->enforce_wordlist, + msg->has_type ? msg->type : 0, + msg->has_u2f_counter ? msg->u2f_counter : 0, + dry_run + ); +} + +void fsm_msgWordAck(WordAck *msg) +{ + recovery_word(msg->word); +} + +void fsm_msgSetU2FCounter(SetU2FCounter *msg) +{ + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you want to set"), _("the U2F counter?"), NULL, NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + storage_setU2FCounter(msg->u2f_counter); + storage_update(); + fsm_sendSuccess(_("U2F counter set")); + layoutHome(); +} \ No newline at end of file diff --git a/firmware/fsm_msg_crypto.h b/firmware/fsm_msg_crypto.h new file mode 100644 index 0000000000..c7fbc5cce8 --- /dev/null +++ b/firmware/fsm_msg_crypto.h @@ -0,0 +1,337 @@ +void fsm_msgCipherKeyValue(CipherKeyValue *msg) +{ + CHECK_INITIALIZED + + CHECK_PARAM(msg->has_key, _("No key provided")); + CHECK_PARAM(msg->has_value, _("No value provided")); + CHECK_PARAM(msg->value.size % 16 == 0, _("Value length must be a multiple of 16")); + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + + bool encrypt = msg->has_encrypt && msg->encrypt; + bool ask_on_encrypt = msg->has_ask_on_encrypt && msg->ask_on_encrypt; + bool ask_on_decrypt = msg->has_ask_on_decrypt && msg->ask_on_decrypt; + if ((encrypt && ask_on_encrypt) || (!encrypt && ask_on_decrypt)) { + layoutCipherKeyValue(encrypt, msg->key); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + uint8_t data[256 + 4]; + strlcpy((char *)data, msg->key, sizeof(data)); + strlcat((char *)data, ask_on_encrypt ? "E1" : "E0", sizeof(data)); + strlcat((char *)data, ask_on_decrypt ? "D1" : "D0", sizeof(data)); + + hmac_sha512(node->private_key, 32, data, strlen((char *)data), data); + + RESP_INIT(CipheredKeyValue); + if (encrypt) { + aes_encrypt_ctx ctx; + aes_encrypt_key256(data, &ctx); + aes_cbc_encrypt(msg->value.bytes, resp->value.bytes, msg->value.size, ((msg->iv.size == 16) ? (msg->iv.bytes) : (data + 32)), &ctx); + } else { + aes_decrypt_ctx ctx; + aes_decrypt_key256(data, &ctx); + aes_cbc_decrypt(msg->value.bytes, resp->value.bytes, msg->value.size, ((msg->iv.size == 16) ? (msg->iv.bytes) : (data + 32)), &ctx); + } + resp->has_value = true; + resp->value.size = msg->value.size; + msg_write(MessageType_MessageType_CipheredKeyValue, resp); + layoutHome(); +} + +void fsm_msgSignIdentity(SignIdentity *msg) +{ + RESP_INIT(SignedIdentity); + + CHECK_INITIALIZED + + layoutSignIdentity(&(msg->identity), msg->has_challenge_visual ? msg->challenge_visual : 0); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + uint8_t hash[32]; + if (!msg->has_identity || cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); + layoutHome(); + return; + } + + uint32_t address_n[5]; + address_n[0] = 0x80000000 | 13; + address_n[1] = 0x80000000 | hash[ 0] | (hash[ 1] << 8) | (hash[ 2] << 16) | ((uint32_t) hash[ 3] << 24); + address_n[2] = 0x80000000 | hash[ 4] | (hash[ 5] << 8) | (hash[ 6] << 16) | ((uint32_t) hash[ 7] << 24); + address_n[3] = 0x80000000 | hash[ 8] | (hash[ 9] << 8) | (hash[10] << 16) | ((uint32_t) hash[11] << 24); + address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | ((uint32_t) hash[15] << 24); + + const char *curve = SECP256K1_NAME; + if (msg->has_ecdsa_curve_name) { + curve = msg->ecdsa_curve_name; + } + HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL); + if (!node) return; + + bool sign_ssh = msg->identity.has_proto && (strcmp(msg->identity.proto, "ssh") == 0); + bool sign_gpg = msg->identity.has_proto && (strcmp(msg->identity.proto, "gpg") == 0); + + int result = 0; + layoutProgressSwipe(_("Signing"), 0); + if (sign_ssh) { // SSH does not sign visual challenge + result = sshMessageSign(node, msg->challenge_hidden.bytes, msg->challenge_hidden.size, resp->signature.bytes); + } else if (sign_gpg) { // GPG should sign a message digest + result = gpgMessageSign(node, msg->challenge_hidden.bytes, msg->challenge_hidden.size, resp->signature.bytes); + } else { + uint8_t digest[64]; + sha256_Raw(msg->challenge_hidden.bytes, msg->challenge_hidden.size, digest); + sha256_Raw((const uint8_t *)msg->challenge_visual, strlen(msg->challenge_visual), digest + 32); + result = cryptoMessageSign(&(coins[0]), node, InputScriptType_SPENDADDRESS, digest, 64, resp->signature.bytes); + } + + if (result == 0) { + hdnode_fill_public_key(node); + if (strcmp(curve, SECP256K1_NAME) != 0) { + resp->has_address = false; + } else { + resp->has_address = true; + hdnode_get_address(node, 0x00, resp->address, sizeof(resp->address)); // hardcoded Bitcoin address type + } + resp->has_public_key = true; + resp->public_key.size = 33; + memcpy(resp->public_key.bytes, node->public_key, 33); + if (node->public_key[0] == 1) { + /* ed25519 public key */ + resp->public_key.bytes[0] = 0; + } + resp->has_signature = true; + resp->signature.size = 65; + msg_write(MessageType_MessageType_SignedIdentity, resp); + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Error signing identity")); + } + layoutHome(); +} + +void fsm_msgGetECDHSessionKey(GetECDHSessionKey *msg) +{ + RESP_INIT(ECDHSessionKey); + + CHECK_INITIALIZED + + layoutDecryptIdentity(&msg->identity); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + uint8_t hash[32]; + if (!msg->has_identity || cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); + layoutHome(); + return; + } + + uint32_t address_n[5]; + address_n[0] = 0x80000000 | 17; + address_n[1] = 0x80000000 | hash[ 0] | (hash[ 1] << 8) | (hash[ 2] << 16) | ((uint32_t) hash[ 3] << 24); + address_n[2] = 0x80000000 | hash[ 4] | (hash[ 5] << 8) | (hash[ 6] << 16) | ((uint32_t) hash[ 7] << 24); + address_n[3] = 0x80000000 | hash[ 8] | (hash[ 9] << 8) | (hash[10] << 16) | ((uint32_t) hash[11] << 24); + address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | ((uint32_t) hash[15] << 24); + + const char *curve = SECP256K1_NAME; + if (msg->has_ecdsa_curve_name) { + curve = msg->ecdsa_curve_name; + } + + const HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL); + if (!node) return; + + int result_size = 0; + if (hdnode_get_shared_key(node, msg->peer_public_key.bytes, resp->session_key.bytes, &result_size) == 0) { + resp->has_session_key = true; + resp->session_key.size = result_size; + msg_write(MessageType_MessageType_ECDHSessionKey, resp); + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Error getting ECDH session key")); + } + layoutHome(); +} + +/* ECIES disabled +void fsm_msgEncryptMessage(EncryptMessage *msg) +{ + CHECK_INITIALIZED + + CHECK_PARAM(msg->has_pubkey, _("No public key provided")); + CHECK_PARAM(msg->has_message, _("No message provided")); + CHECK_PARAM(msg->pubkey.size == 33, _("Invalid public key provided")); + curve_point pubkey; + CHECK_PARAM(ecdsa_read_pubkey(&secp256k1, msg->pubkey.bytes, &pubkey) == 1, _("Invalid public key provided")); + + bool display_only = msg->has_display_only && msg->display_only; + bool signing = msg->address_n_count > 0; + RESP_INIT(EncryptedMessage); + const HDNode *node = 0; + uint8_t address_raw[MAX_ADDR_RAW_SIZE]; + if (signing) { + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + + CHECK_PIN + + node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + hdnode_get_address_raw(node, coin->address_type, address_raw); + } + layoutEncryptMessage(msg->message.bytes, msg->message.size, signing); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + layoutProgressSwipe(_("Encrypting"), 0); + if (cryptoMessageEncrypt(&pubkey, msg->message.bytes, msg->message.size, display_only, resp->nonce.bytes, &(resp->nonce.size), resp->message.bytes, &(resp->message.size), resp->hmac.bytes, &(resp->hmac.size), signing ? node->private_key : 0, signing ? address_raw : 0) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Error encrypting message")); + layoutHome(); + return; + } + resp->has_nonce = true; + resp->has_message = true; + resp->has_hmac = true; + msg_write(MessageType_MessageType_EncryptedMessage, resp); + layoutHome(); +} + +void fsm_msgDecryptMessage(DecryptMessage *msg) +{ + CHECK_INITIALIZED + + CHECK_PARAM(msg->has_nonce, _("No nonce provided")); + CHECK_PARAM(msg->has_message, _("No message provided")); + CHECK_PARAM(msg->has_hmac, _("No message hmac provided")); + + CHECK_PARAM(msg->nonce.size == 33, _("Invalid nonce key provided")); + curve_point nonce_pubkey; + CHECK_PARAM(ecdsa_read_pubkey(&secp256k1, msg->nonce.bytes, &nonce_pubkey) == 1, _("Invalid nonce provided")); + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + + layoutProgressSwipe(_("Decrypting"), 0); + RESP_INIT(DecryptedMessage); + bool display_only = false; + bool signing = false; + uint8_t address_raw[MAX_ADDR_RAW_SIZE]; + if (cryptoMessageDecrypt(&nonce_pubkey, msg->message.bytes, msg->message.size, msg->hmac.bytes, msg->hmac.size, node->private_key, resp->message.bytes, &(resp->message.size), &display_only, &signing, address_raw) != 0) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + if (signing) { + base58_encode_check(address_raw, 21, resp->address, sizeof(resp->address)); + } + layoutDecryptMessage(resp->message.bytes, resp->message.size, signing ? resp->address : 0); + protectButton(ButtonRequestType_ButtonRequest_Other, true); + if (display_only) { + resp->has_address = false; + resp->has_message = false; + memset(resp->address, 0, sizeof(resp->address)); + memset(&(resp->message), 0, sizeof(resp->message)); + } else { + resp->has_address = signing; + resp->has_message = true; + } + msg_write(MessageType_MessageType_DecryptedMessage, resp); + layoutHome(); +} +*/ + +void fsm_msgCosiCommit(CosiCommit *msg) +{ + RESP_INIT(CosiCommitment); + + CHECK_INITIALIZED + + CHECK_PARAM(msg->has_data, _("No data provided")); + + layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, msg->data.size, false); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + + uint8_t nonce[32]; + sha256_Raw(msg->data.bytes, msg->data.size, nonce); + rfc6979_state rng; + init_rfc6979(node->private_key, nonce, &rng); + generate_rfc6979(nonce, &rng); + + resp->has_commitment = true; + resp->has_pubkey = true; + resp->commitment.size = 32; + resp->pubkey.size = 32; + + ed25519_publickey(nonce, resp->commitment.bytes); + ed25519_publickey(node->private_key, resp->pubkey.bytes); + + msg_write(MessageType_MessageType_CosiCommitment, resp); + layoutHome(); +} + +void fsm_msgCosiSign(CosiSign *msg) +{ + RESP_INIT(CosiSignature); + + CHECK_INITIALIZED + + CHECK_PARAM(msg->has_data, _("No data provided")); + CHECK_PARAM(msg->has_global_commitment && msg->global_commitment.size == 32, _("Invalid global commitment")); + CHECK_PARAM(msg->has_global_pubkey && msg->global_pubkey.size == 32, _("Invalid global pubkey")); + + layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, msg->data.size, true); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + + uint8_t nonce[32]; + sha256_Raw(msg->data.bytes, msg->data.size, nonce); + rfc6979_state rng; + init_rfc6979(node->private_key, nonce, &rng); + generate_rfc6979(nonce, &rng); + + resp->has_signature = true; + resp->signature.size = 32; + + ed25519_cosi_sign(msg->data.bytes, msg->data.size, node->private_key, nonce, msg->global_commitment.bytes, msg->global_pubkey.bytes, resp->signature.bytes); + + msg_write(MessageType_MessageType_CosiSignature, resp); + layoutHome(); +} \ No newline at end of file diff --git a/firmware/fsm_msg_debug.h b/firmware/fsm_msg_debug.h new file mode 100644 index 0000000000..3a84a87153 --- /dev/null +++ b/firmware/fsm_msg_debug.h @@ -0,0 +1,97 @@ +#if DEBUG_LINK + +void fsm_msgDebugLinkGetState(DebugLinkGetState *msg) +{ + (void)msg; + + // Do not use RESP_INIT because it clears msg_resp, but another message + // might be being handled + DebugLinkState resp; + memset(&resp, 0, sizeof(resp)); + + resp.has_layout = true; + resp.layout.size = OLED_BUFSIZE; + memcpy(resp.layout.bytes, oledGetBuffer(), OLED_BUFSIZE); + + if (storage_hasPin()) { + resp.has_pin = true; + strlcpy(resp.pin, storage_getPin(), sizeof(resp.pin)); + } + + resp.has_matrix = true; + strlcpy(resp.matrix, pinmatrix_get(), sizeof(resp.matrix)); + + resp.has_reset_entropy = true; + resp.reset_entropy.size = reset_get_int_entropy(resp.reset_entropy.bytes); + + resp.has_reset_word = true; + strlcpy(resp.reset_word, reset_get_word(), sizeof(resp.reset_word)); + + resp.has_recovery_fake_word = true; + strlcpy(resp.recovery_fake_word, recovery_get_fake_word(), sizeof(resp.recovery_fake_word)); + + resp.has_recovery_word_pos = true; + resp.recovery_word_pos = recovery_get_word_pos(); + + if (storage_hasMnemonic()) { + resp.has_mnemonic = true; + strlcpy(resp.mnemonic, storage_getMnemonic(), sizeof(resp.mnemonic)); + } + + if (storage_hasNode()) { + resp.has_node = true; + storage_dumpNode(&(resp.node)); + } + + resp.has_passphrase_protection = true; + resp.passphrase_protection = storage_hasPassphraseProtection(); + + msg_debug_write(MessageType_MessageType_DebugLinkState, &resp); +} + +void fsm_msgDebugLinkStop(DebugLinkStop *msg) +{ + (void)msg; +} + +void fsm_msgDebugLinkMemoryRead(DebugLinkMemoryRead *msg) +{ + RESP_INIT(DebugLinkMemory); + + uint32_t length = 1024; + if (msg->has_length && msg->length < length) + length = msg->length; + resp->has_memory = true; + memcpy(resp->memory.bytes, FLASH_PTR(msg->address), length); + resp->memory.size = length; + msg_debug_write(MessageType_MessageType_DebugLinkMemory, resp); +} + +void fsm_msgDebugLinkMemoryWrite(DebugLinkMemoryWrite *msg) +{ + uint32_t length = msg->memory.size; + if (msg->flash) { + svc_flash_unlock(); + svc_flash_program(FLASH_CR_PROGRAM_X32); + for (uint32_t i = 0; i < length; i += 4) { + uint32_t word; + memcpy(&word, msg->memory.bytes + i, 4); + flash_write32(msg->address + i, word); + } + uint32_t dummy = svc_flash_lock(); + (void)dummy; + } else { +#if !EMULATOR + memcpy((void *) msg->address, msg->memory.bytes, length); +#endif + } +} + +void fsm_msgDebugLinkFlashErase(DebugLinkFlashErase *msg) +{ + svc_flash_unlock(); + svc_flash_erase_sector(msg->sector); + uint32_t dummy = svc_flash_lock(); + (void)dummy; +} +#endif diff --git a/firmware/fsm_msg_ethereum.h b/firmware/fsm_msg_ethereum.h new file mode 100644 index 0000000000..0c9420e3fe --- /dev/null +++ b/firmware/fsm_msg_ethereum.h @@ -0,0 +1,99 @@ +void fsm_msgEthereumSignTx(EthereumSignTx *msg) +{ + CHECK_INITIALIZED + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + + ethereum_signing_init(msg, node); +} + +void fsm_msgEthereumTxAck(EthereumTxAck *msg) +{ + ethereum_signing_txack(msg); +} + +void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) +{ + RESP_INIT(EthereumAddress); + + CHECK_INITIALIZED + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + + resp->address.size = 20; + + if (!hdnode_get_ethereum_pubkeyhash(node, resp->address.bytes)) + return; + + if (msg->has_show_display && msg->show_display) { + char desc[16]; + strlcpy(desc, "Address:", sizeof(desc)); + + char address[43] = { '0', 'x' }; + ethereum_address_checksum(resp->address.bytes, address + 2); + + if (!fsm_layoutAddress(address, desc, false, 0, msg->address_n, msg->address_n_count)) { + return; + } + } + + msg_write(MessageType_MessageType_EthereumAddress, resp); + layoutHome(); +} + +void fsm_msgEthereumSignMessage(EthereumSignMessage *msg) +{ + RESP_INIT(EthereumMessageSignature); + + CHECK_INITIALIZED + + layoutSignMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); + if (!node) return; + + ethereum_message_sign(msg, node, resp); + layoutHome(); +} + +void fsm_msgEthereumVerifyMessage(EthereumVerifyMessage *msg) +{ + CHECK_PARAM(msg->has_address, _("No address provided")); + CHECK_PARAM(msg->has_message, _("No message provided")); + + if (ethereum_message_verify(msg) != 0) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); + return; + } + + char address[43] = { '0', 'x' }; + ethereum_address_checksum(msg->address.bytes, address + 2); + layoutVerifyAddress(address); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + layoutVerifyMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + fsm_sendSuccess(_("Message verified")); + + layoutHome(); +} \ No newline at end of file diff --git a/firmware/fsm_msg_nem.h b/firmware/fsm_msg_nem.h new file mode 100644 index 0000000000..25fae20cc8 --- /dev/null +++ b/firmware/fsm_msg_nem.h @@ -0,0 +1,287 @@ +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); + + 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)) + 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)) { + 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) + + CHECK_PARAM(msg->has_transaction, _("No common provided")); + + // 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); + + HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n, msg->transaction.address_n_count, NULL); + if (!node) return; + + hdnode_fill_public_key(node); + + 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) { + msg->transfer.mosaics_count = nem_canonicalizeMosaics(msg->transfer.mosaics, msg->transfer.mosaics_count); + } + + 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->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(); +} + +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")); + + 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; + } + + CHECK_PIN + + 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->has_payload = true; + 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(); +} \ No newline at end of file diff --git a/firmware/fsm_msg_stellar.h b/firmware/fsm_msg_stellar.h new file mode 100644 index 0000000000..d60191da47 --- /dev/null +++ b/firmware/fsm_msg_stellar.h @@ -0,0 +1,273 @@ +void fsm_msgStellarGetPublicKey(StellarGetPublicKey *msg) +{ + RESP_INIT(StellarPublicKey); + + CHECK_INITIALIZED + + CHECK_PIN + + // Will exit if the user does not confirm + stellar_layoutGetPublicKey(msg->address_n, msg->address_n_count); + + // Read public key and write it to the response + resp->has_public_key = true; + resp->public_key.size = 32; + stellar_getPubkeyAtAddress(msg->address_n, msg->address_n_count, resp->public_key.bytes, sizeof(resp->public_key.bytes)); + + msg_write(MessageType_MessageType_StellarPublicKey, resp); + + layoutHome(); +} + +void fsm_msgStellarSignMessage(StellarSignMessage *msg) +{ + CHECK_INITIALIZED + CHECK_PIN + + RESP_INIT(StellarMessageSignature); + + // Will exit if the user does not confirm + stellar_confirmSignString(msg, resp); + + msg_write(MessageType_MessageType_StellarMessageSignature, resp); + + layoutHome(); +} + +void fsm_msgStellarVerifyMessage(StellarVerifyMessage *msg) +{ + if (!stellar_verifySignature(msg)) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); + return; + } + + fsm_sendSuccess(_("Message verified")); + layoutHome(); +} + +void fsm_msgStellarSignTx(StellarSignTx *msg) +{ + CHECK_INITIALIZED + CHECK_PIN + + stellar_signingInit(msg); + + // Confirm transaction basics + stellar_layoutTransactionSummary(msg); + + // Respond with a request for the first operation + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); +} + +void fsm_msgStellarCreateAccountOp(StellarCreateAccountOp *msg) +{ + stellar_confirmCreateAccountOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarPaymentOp(StellarPaymentOp *msg) +{ + // This will display additional dialogs to the user + stellar_confirmPaymentOp(msg); + + // Last operation was confirmed, send a StellarSignedTx + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarPathPaymentOp(StellarPathPaymentOp *msg) +{ + stellar_confirmPathPaymentOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarManageOfferOp(StellarManageOfferOp *msg) +{ + stellar_confirmManageOfferOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarCreatePassiveOfferOp(StellarCreatePassiveOfferOp *msg) +{ + stellar_confirmCreatePassiveOfferOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarSetOptionsOp(StellarSetOptionsOp *msg) +{ + stellar_confirmSetOptionsOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarChangeTrustOp(StellarChangeTrustOp *msg) +{ + stellar_confirmChangeTrustOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarAllowTrustOp(StellarAllowTrustOp *msg) +{ + stellar_confirmAllowTrustOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarAccountMergeOp(StellarAccountMergeOp *msg) +{ + stellar_confirmAccountMergeOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarManageDataOp(StellarManageDataOp *msg) +{ + stellar_confirmManageDataOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarBumpSequenceOp(StellarBumpSequenceOp *msg) +{ + stellar_confirmBumpSequenceOp(msg); + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} \ No newline at end of file