Example #1
0
void fsm_msgChangePin(ChangePin *msg)
{
    bool removal = msg->has_remove && msg->remove;
    bool confirmed = false;

    if(removal)
    {
        if(storage_has_pin())
        {
            confirmed = confirm(ButtonRequestType_ButtonRequest_RemovePin,
                                "Remove PIN", "Do you want to remove PIN protection?");
        }
        else
        {
            fsm_sendSuccess("PIN removed");
            return;
        }
    }
    else
    {
        if(storage_has_pin())
            confirmed = confirm(ButtonRequestType_ButtonRequest_ChangePin,
                                "Change PIN", "Do you want to change your PIN?");
        else
            confirmed = confirm(ButtonRequestType_ButtonRequest_CreatePin,
                                "Create PIN", "Do you want to add PIN protection?");
    }

    if(!confirmed)
    {
        fsm_sendFailure(FailureType_Failure_ActionCancelled,
                        removal ? "PIN removal cancelled" : "PIN change cancelled");
        go_home();
        return;
    }

    if(!pin_protect("Enter Current PIN"))
    {
        go_home();
        return;
    }

    if(removal)
    {
        storage_set_pin(0);
        storage_commit();
        fsm_sendSuccess("PIN removed");
    }
    else
    {
        if(change_pin())
        {
            storage_commit();
            fsm_sendSuccess("PIN changed");
        }
    }

    go_home();
}
Example #2
0
void fsm_msgLoadDevice(LoadDevice *msg)
{
	if (storage_isInitialized()) {
		fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Device is already initialized. Use Wipe first.");
		return;
	}

	layoutDialogSwipe(DIALOG_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, "Load cancelled");
		layoutHome();
		return;
	}

	if (msg->has_mnemonic && !(msg->has_skip_checksum && msg->skip_checksum) ) {
		if (!mnemonic_check(msg->mnemonic)) {
			fsm_sendFailure(FailureType_Failure_ActionCancelled, "Mnemonic with wrong checksum provided");
			layoutHome();
			return;
		}
	}

	storage_loadDevice(msg);
	storage_commit();
	fsm_sendSuccess("Device loaded");
	layoutHome();
}
Example #3
0
void fsm_msgClearSession(ClearSession *msg)
{
	(void)msg;
	session_clear(true); // clear PIN as well
	layoutScreensaver();
	fsm_sendSuccess("Session cleared");
}
Example #4
0
void fsm_msgApplySettings(ApplySettings *msg)
{
	if (msg->has_label && msg->has_language) {
		layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change label to", msg->label, "and language to", msg->language, "?");
	} else
	if (msg->has_label) {
		layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change label to", msg->label, "?", NULL, NULL);
	} else
	if (msg->has_language) {
		layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change language to", msg->language, "?", NULL, NULL);
	} else {
		fsm_sendFailure(FailureType_Failure_SyntaxError, "No setting provided");
		return;
	}
	if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
		fsm_sendFailure(FailureType_Failure_ActionCancelled, "Apply settings cancelled");
		layoutHome();
		return;
	}
	if (!protectPin(true)) {
		layoutHome();
		return;
	}
	if (msg->has_label) {
		storage_setLabel(msg->label);
	}
	if (msg->has_language) {
		storage_setLanguage(msg->language);
	}
	storage_commit();
	fsm_sendSuccess("Settings applied");
	layoutHome();
}
Example #5
0
void fsm_msgLoadDevice(LoadDevice *msg)
{
    if(storage_is_initialized())
    {
        fsm_sendFailure(FailureType_Failure_UnexpectedMessage,
                        "Device is already initialized. Use Wipe first.");
        return;
    }

    if(!confirm_load_device(msg->has_node))
    {
        fsm_sendFailure(FailureType_Failure_ActionCancelled, "Load cancelled");
        go_home();
        return;
    }

    if(msg->has_mnemonic && !(msg->has_skip_checksum && msg->skip_checksum))
    {
        if(!mnemonic_check(msg->mnemonic))
        {
            fsm_sendFailure(FailureType_Failure_ActionCancelled,
                            "Mnemonic with wrong checksum provided");
            go_home();
            return;
        }
    }

    storage_load_device(msg);

    storage_commit();
    fsm_sendSuccess("Device loaded");
    go_home();
}
Example #6
0
void recovery_word(const char *word)
{
	if (!awaiting_word) {
		fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Recovery mode");
		layoutHome();
		return;
	}

	if (word_pos == 0) { // fake word
		if (strcmp(word, fake_word) != 0) {
			storage_reset();
			fsm_sendFailure(FailureType_Failure_SyntaxError, "Wrong word retyped");
			layoutHome();
			return;
		}
	} else { // real word
		if (enforce_wordlist) { // check if word is valid
			const char * const *wl = mnemonic_wordlist();
			bool found = false;
			while (*wl) {
				if (strcmp(word, *wl) == 0) {
					found = true;
					break;
				}
				wl++;
			}
			if (!found) {
				storage_reset();
				fsm_sendFailure(FailureType_Failure_SyntaxError, "Word not found in a wordlist");
				layoutHome();
				return;
			}
		}
		strlcpy(words[word_pos - 1], word, sizeof(words[word_pos - 1]));
	}

	if (word_index + 1 == 24) { // last one
		uint32_t i;
		strlcpy(storage.mnemonic, words[0], sizeof(storage.mnemonic));
		for (i = 1; i < word_count; i++) {
			strlcat(storage.mnemonic, " ", sizeof(storage.mnemonic));
			strlcat(storage.mnemonic, words[i], sizeof(storage.mnemonic));
		}
		if (!enforce_wordlist || mnemonic_check(storage.mnemonic)) {
			storage.has_mnemonic = true;
			storage_commit();
			fsm_sendSuccess("Device recovered");
		} else {
			storage_reset();
			fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid mnemonic, are words in correct order?");
		}
		awaiting_word = false;
		layoutHome();
	} else {
		word_index++;
		next_word();
	}
}
Example #7
0
void fsm_msgApplySettings(ApplySettings *msg)
{
	if (msg->has_label) {
		layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change label to", msg->label, "?", NULL, NULL);
		if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
			fsm_sendFailure(FailureType_Failure_ActionCancelled, "Apply settings cancelled");
			layoutHome();
			return;
		}
	}
	if (msg->has_language) {
		layoutDialogSwipe(DIALOG_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, "Apply settings cancelled");
			layoutHome();
			return;
		}
	}
	if (msg->has_use_passphrase) {
		layoutDialogSwipe(DIALOG_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, "Apply settings cancelled");
			layoutHome();
			return;
		}
	}
	if (msg->has_homescreen) {
		layoutDialogSwipe(DIALOG_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, "Apply settings cancelled");
			layoutHome();
			return;
		}
	}
	if (!msg->has_label && !msg->has_language && !msg->has_use_passphrase && !msg->has_homescreen) {
		fsm_sendFailure(FailureType_Failure_SyntaxError, "No setting provided");
		return;
	}
	if (!protectPin(true)) {
		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);
	}
	storage_commit();
	fsm_sendSuccess("Settings applied");
	layoutHome();
}
Example #8
0
void fsm_msgVerifyMessage(VerifyMessage *msg)
{
	const char *address = msg->has_address ? msg->address : 0;
	layoutProgressSwipe("Verifying", 0, 0);
	if (msg->signature.size == 65 && transactionMessageVerify(msg->message.bytes, msg->message.size, msg->signature.bytes, address)) {
		layoutVerifyMessage(msg->message.bytes, msg->message.size);
		protectButton(ButtonRequestType_ButtonRequest_Other, true);
		fsm_sendSuccess("Message verified");
	} else {
		fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid signature");
	}
	layoutHome();
}
Example #9
0
void fsm_msgChangePin(ChangePin *msg)
{
	bool removal = msg->has_remove && msg->remove;
	if (removal) {
		if (storage_hasPin()) {
			layoutDialogSwipe(DIALOG_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(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change current PIN?", NULL, NULL, NULL, NULL);
		} else {
			layoutDialogSwipe(DIALOG_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, removal ? "PIN removal cancelled" : "PIN change cancelled");
		layoutHome();
		return;
	}
	if (!protectPin(false)) {
		layoutHome();
		return;
	}
	if (removal) {
		storage_setPin(0);
		fsm_sendSuccess("PIN removed");
	} else {
		if (protectChangePin()) {
			fsm_sendSuccess("PIN changed");
		} else {
			fsm_sendFailure(FailureType_Failure_ActionCancelled, "PIN change failed");
		}
	}
	layoutHome();
}
Example #10
0
void fsm_msgWipeDevice(WipeDevice *msg)
{
	(void)msg;
	layoutDialogSwipe(DIALOG_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, "Wipe cancelled");
		layoutHome();
		return;
	}
	storage_reset();
	storage_reset_uuid();
	storage_commit();
	// 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();
}
Example #11
0
void fsm_msgVerifyMessage(VerifyMessage *msg)
{
    if(!msg->has_address)
    {
        fsm_sendFailure(FailureType_Failure_Other, "No address provided");
        return;
    }

    if(!msg->has_message)
    {
        fsm_sendFailure(FailureType_Failure_Other, "No message provided");
        return;
    }

    const CoinType *coin = fsm_getCoin(msg->coin_name);
    if (!coin) return;

    layout_simple_message("Verifying Message...");

    uint8_t addr_raw[21];

    if(!ecdsa_address_decode(msg->address, addr_raw))
    {
        fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid address");
    }

    if(msg->signature.size == 65 &&
            cryptoMessageVerify(coin, msg->message.bytes, msg->message.size, addr_raw,
                                msg->signature.bytes) == 0)
    {
        if(review(ButtonRequestType_ButtonRequest_Other, "Message Verified",
                  (char *)msg->message.bytes))
        {
            fsm_sendSuccess("Message verified");
        }
    }
    else
    {
        fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid signature");
    }

    go_home();
}
Example #12
0
void fsm_msgWipeDevice(WipeDevice *msg)
{
    (void)msg;

    if(!confirm(ButtonRequestType_ButtonRequest_WipeDevice, "Wipe Device",
                "Do you want to erase your private keys and settings?"))
    {
        fsm_sendFailure(FailureType_Failure_ActionCancelled, "Wipe cancelled");
        go_home();
        return;
    }

    /* Wipe device */
    storage_reset();
    storage_reset_uuid();
    storage_commit();

    fsm_sendSuccess("Device wiped");
    go_home();
}
Example #13
0
void fsm_msgVerifyMessage(VerifyMessage *msg)
{
	if (!msg->has_address) {
		fsm_sendFailure(FailureType_Failure_Other, "No address provided");
		return;
	}
	if (!msg->has_message) {
		fsm_sendFailure(FailureType_Failure_Other, "No message provided");
		return;
	}
	layoutProgressSwipe("Verifying", 0);
	uint8_t addr_raw[21];
	if (!ecdsa_address_decode(msg->address, addr_raw)) {
		fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid address");
	}
	if (msg->signature.size == 65 && cryptoMessageVerify(msg->message.bytes, msg->message.size, addr_raw, msg->signature.bytes) == 0) {
		layoutVerifyMessage(msg->message.bytes, msg->message.size);
		protectButton(ButtonRequestType_ButtonRequest_Other, true);
		fsm_sendSuccess("Message verified");
	} else {
		fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid signature");
	}
	layoutHome();
}
Example #14
0
void reset_entropy(const uint8_t *ext_entropy, uint32_t len)
{
    if(!awaiting_entropy)
    {
        fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Reset mode");
        return;
    }

    SHA256_CTX ctx;
    sha256_Init(&ctx);
    sha256_Update(&ctx, int_entropy, 32);
    sha256_Update(&ctx, ext_entropy, len);
    sha256_Final(int_entropy, &ctx);

    const char *temp_mnemonic = mnemonic_from_data(int_entropy, strength / 8);

    memset(int_entropy, 0, 32);
    awaiting_entropy = false;

    /*
     * Format mnemonic for user review
     */
    uint32_t word_count = 0, current_page = 0, page_count;
    char *tok;
    char tokened_mnemonic[TOKENED_MNEMONIC_BUF];
    char mnemonic_by_screen[MAX_PAGES][MNEMONIC_BY_SCREEN_BUF];
    char formatted_mnemonic[MAX_PAGES][FORMATTED_MNEMONIC_BUF];
    char mnemonic_display[FORMATTED_MNEMONIC_BUF];
    char formatted_word[MAX_WORD_LEN + ADDITIONAL_WORD_PAD];

    strlcpy(tokened_mnemonic, temp_mnemonic, TOKENED_MNEMONIC_BUF);

    tok = strtok(tokened_mnemonic, " ");

    while(tok)
    {
        snprintf(formatted_word, MAX_WORD_LEN + ADDITIONAL_WORD_PAD, "%lu.%s",
                 (unsigned long)(word_count + 1), tok);

        /* Check that we have enough room on display to show word */
        snprintf(mnemonic_display, FORMATTED_MNEMONIC_BUF, "%s   %s",
                 formatted_mnemonic[current_page], formatted_word);

        if(calc_str_line(get_body_font(), mnemonic_display, BODY_WIDTH) > 3)
        {
            page_count++;
            current_page++;

            snprintf(mnemonic_display, FORMATTED_MNEMONIC_BUF, "%s   %s",
                 formatted_mnemonic[current_page], formatted_word);
        }


        strlcpy(formatted_mnemonic[current_page], mnemonic_display,
                FORMATTED_MNEMONIC_BUF);

        /* Save mnemonic for each screen */
        if(strlen(mnemonic_by_screen[current_page]) == 0)
        {
            strlcpy(mnemonic_by_screen[current_page], tok, MNEMONIC_BY_SCREEN_BUF);
        }
        else
        {
            strlcat(mnemonic_by_screen[current_page], " ", MNEMONIC_BY_SCREEN_BUF);
            strlcat(mnemonic_by_screen[current_page], tok, MNEMONIC_BY_SCREEN_BUF);
        }

        tok = strtok(NULL, " ");
        word_count++;
    }

    /* Have user confirm mnemonic is sets of 12 words */
    for(page_count = current_page + 1, current_page = 0; current_page < page_count; current_page++)
    {
        char title[MEDIUM_STR_BUF] = "Recovery Sentence";

        /* make current screen mnemonic available via debuglink */
        strlcpy(current_words, mnemonic_by_screen[current_page], MNEMONIC_BY_SCREEN_BUF);

        if(page_count > 1)
        {
            /* snprintf: 20 + 10 (%d) + 1 (NULL) = 31 */
            snprintf(title, MEDIUM_STR_BUF, "Recovery Sentence %lu/%lu", current_page + 1, page_count);
        }

        if(!confirm(ButtonRequestType_ButtonRequest_ConfirmWord, title, "%s",
                    formatted_mnemonic[current_page]))
        {
            fsm_sendFailure(FailureType_Failure_ActionCancelled, "Reset cancelled");
            storage_reset();
            go_home();
            return;
        }
    }

    /* Save mnemonic */
    storage_set_mnemonic(temp_mnemonic);
    storage_commit();

    fsm_sendSuccess("Device reset");
    go_home();
}
Example #15
0
void fsm_msgApplySettings(ApplySettings *msg)
{
    if(msg->has_label)
    {
        if(!confirm(ButtonRequestType_ButtonRequest_ChangeLabel,
                    "Change Label", "Do you want to change the label to \"%s\"?", msg->label))
        {
            fsm_sendFailure(FailureType_Failure_ActionCancelled,
                            "Apply settings cancelled");
            go_home();
            return;
        }
    }

    if(msg->has_language)
    {
        if(!confirm(ButtonRequestType_ButtonRequest_ChangeLanguage,
                    "Change Language", "Do you want to change the language to %s?", msg->language))
        {
            fsm_sendFailure(FailureType_Failure_ActionCancelled,
                            "Apply settings cancelled");
            go_home();
            return;
        }
    }

    if(msg->has_use_passphrase)
    {
        if(msg->use_passphrase)
        {
            if(!confirm(ButtonRequestType_ButtonRequest_EnablePassphrase,
                        "Enable Passphrase", "Do you want to enable passphrase encryption?",
                        msg->language))
            {
                fsm_sendFailure(FailureType_Failure_ActionCancelled,
                                "Apply settings cancelled");
                go_home();
                return;
            }
        }
        else
        {
            if(!confirm(ButtonRequestType_ButtonRequest_DisablePassphrase,
                        "Disable Passphrase", "Do you want to disable passphrase encryption?",
                        msg->language))
            {
                fsm_sendFailure(FailureType_Failure_ActionCancelled,
                                "Apply settings cancelled");
                go_home();
                return;
            }
        }
    }

    if(!msg->has_label && !msg->has_language && !msg->has_use_passphrase)
    {
        fsm_sendFailure(FailureType_Failure_SyntaxError, "No setting provided");
        return;
    }

    if(!pin_protect_cached())
    {
        go_home();
        return;
    }

    if(msg->has_label)
    {
        storage_set_label(msg->label);
    }

    if(msg->has_language)
    {
        storage_set_language(msg->language);
    }

    if(msg->has_use_passphrase)
    {
        storage_set_passphrase_protected(msg->use_passphrase);
    }

    storage_commit();

    fsm_sendSuccess("Settings applied");
    go_home();
}
Example #16
0
void reset_entropy(const uint8_t *ext_entropy, uint32_t len)
{
    if (!awaiting_entropy) {
        fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Reset mode");
        return;
    }
    SHA256_CTX ctx;
    sha256_Init(&ctx);
    sha256_Update(&ctx, int_entropy, 32);
    sha256_Update(&ctx, ext_entropy, len);
    sha256_Final(int_entropy, &ctx);
    strlcpy(storage.mnemonic, mnemonic_from_data(int_entropy, strength / 8), sizeof(storage.mnemonic));
    memset(int_entropy, 0, 32);
    awaiting_entropy = false;

    int pass, word_pos, i = 0, j;

    for (pass = 0; pass < 2; pass++) {
        i = 0;
        for (word_pos = 1; word_pos <= (int)strength/32*3; word_pos++) {
            // copy current_word
            j = 0;
            while (storage.mnemonic[i] != ' ' && storage.mnemonic[i] != 0 && j + 1 < (int)sizeof(current_word)) {
                current_word[j] = storage.mnemonic[i];
                i++;
                j++;
            }
            current_word[j] = 0;
            if (storage.mnemonic[i] != 0) i++;
            char desc[] = "##th word is:";
            if (word_pos < 10) {
                desc[0] = ' ';
            } else {
                desc[0] = '0' + word_pos / 10;
            }
            desc[1] = '0' + word_pos % 10;
            if (word_pos == 1 || word_pos == 21) {
                desc[2] = 's';
                desc[3] = 't';
            } else if (word_pos == 2 || word_pos == 22) {
                desc[2] = 'n';
                desc[3] = 'd';
            } else if (word_pos == 3 || word_pos == 23) {
                desc[2] = 'r';
                desc[3] = 'd';
            }
            if (word_pos == (int)strength/32*3) { // last word
                if (pass == 1) {
                    layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Finish", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, current_word, NULL);
                } else {
                    layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Again", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, current_word, NULL);
                }
            } else {
                if (pass == 1) {
                    layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, current_word, NULL);
                } else {
                    layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, current_word, NULL);
                }
            }
            if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmWord, true)) {
                storage_reset();
                layoutHome();
                return;
            }
        }
    }

    storage.has_mnemonic = true;
    storage_commit();
    fsm_sendSuccess("Device reset");
    layoutHome();
}
Example #17
0
void fsm_msgClearSession(ClearSession *msg)
{
	(void)msg;
	session_clear();
	fsm_sendSuccess("Session cleared");
}
Example #18
0
void recovery_word(const char *word)
{
    if (!awaiting_word) 
    {
        fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Recovery mode");
        go_home();
        return;
    }

    if (word_pos == 0) 
    { // fake word
        if (strcmp(word, fake_word) != 0) {
            storage_reset();
            fsm_sendFailure(FailureType_Failure_SyntaxError, "Wrong word retyped");
            go_home();
            return;
        }
    } else { // real word
        if (enforce_wordlist) 
        { // check if word is valid
            const char * const *wl = mnemonic_wordlist();
            bool found = false;
            while (*wl) 
            {
                if (strcmp(word, *wl) == 0) 
                {
                    found = true;
                    break;
                }
                wl++;
            }
            if (!found) 
            {
                storage_reset();
                fsm_sendFailure(FailureType_Failure_SyntaxError, "Word not found in a wordlist");
                go_home();
                return;
            }
        }
        strlcpy(words[word_pos - 1], word, sizeof(words[word_pos - 1]));
    }

    if (word_index + 1 == 24)
    { // last one
        storage_set_mnemonic_from_words((const char (*)[])words, word_count);

        if (!enforce_wordlist || mnemonic_check(storage_get_shadow_mnemonic()))
        {
        	storage_commit();
            fsm_sendSuccess("Device recovered");
        } else {
            storage_reset();
            fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid mnemonic, are words in correct order?");
        }
        awaiting_word = false;
        go_home();
    } else {
        word_index++;
        next_word();
    }
}