2018-09-06 16:28:56 +00:00
|
|
|
/*
|
2019-06-17 18:27:55 +00:00
|
|
|
* This file is part of the Trezor project, https://trezor.io/
|
2018-09-06 16:28:56 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 2018 Pavol Rusnak <stick@satoshilabs.com>
|
|
|
|
*
|
|
|
|
* This library is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgInitialize(const 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);
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgGetFeatures(const GetFeatures *msg) {
|
|
|
|
(void)msg;
|
|
|
|
RESP_INIT(Features);
|
|
|
|
resp->has_vendor = true;
|
|
|
|
strlcpy(resp->vendor, "trezor.io", 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, config_uuid_str, sizeof(resp->device_id));
|
|
|
|
resp->has_pin_protection = true;
|
|
|
|
resp->pin_protection = config_hasPin();
|
|
|
|
resp->has_passphrase_protection = true;
|
|
|
|
config_getPassphraseProtection(&(resp->passphrase_protection));
|
2018-05-03 14:47:37 +00:00
|
|
|
#ifdef SCM_REVISION
|
2019-03-29 16:10:31 +00:00
|
|
|
int len = sizeof(SCM_REVISION) - 1;
|
|
|
|
resp->has_revision = true;
|
|
|
|
memcpy(resp->revision.bytes, SCM_REVISION, len);
|
|
|
|
resp->revision.size = len;
|
2018-05-03 14:47:37 +00:00
|
|
|
#endif
|
2019-03-29 16:10:31 +00:00
|
|
|
resp->has_bootloader_hash = true;
|
|
|
|
resp->bootloader_hash.size =
|
|
|
|
memory_bootloader_hash(resp->bootloader_hash.bytes);
|
|
|
|
|
|
|
|
resp->has_language =
|
|
|
|
config_getLanguage(resp->language, sizeof(resp->language));
|
|
|
|
resp->has_label = config_getLabel(resp->label, sizeof(resp->label));
|
|
|
|
resp->has_initialized = true;
|
|
|
|
resp->initialized = config_isInitialized();
|
|
|
|
resp->has_imported = config_getImported(&(resp->imported));
|
|
|
|
resp->has_pin_cached = true;
|
|
|
|
resp->pin_cached = session_isUnlocked() && config_hasPin();
|
|
|
|
resp->has_passphrase_cached = true;
|
|
|
|
resp->passphrase_cached = session_isPassphraseCached();
|
|
|
|
resp->has_needs_backup = true;
|
|
|
|
config_getNeedsBackup(&(resp->needs_backup));
|
|
|
|
resp->has_unfinished_backup = true;
|
|
|
|
config_getUnfinishedBackup(&(resp->unfinished_backup));
|
|
|
|
resp->has_no_backup = true;
|
|
|
|
config_getNoBackup(&(resp->no_backup));
|
|
|
|
resp->has_flags = config_getFlags(&(resp->flags));
|
|
|
|
resp->has_model = true;
|
|
|
|
strlcpy(resp->model, "1", sizeof(resp->model));
|
|
|
|
|
|
|
|
msg_write(MessageType_MessageType_Features, resp);
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgPing(const 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();
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgChangePin(const ChangePin *msg) {
|
|
|
|
bool removal = msg->has_remove && msg->remove;
|
|
|
|
if (removal) {
|
|
|
|
if (config_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 (config_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (protectChangePin(removal)) {
|
|
|
|
if (removal) {
|
|
|
|
fsm_sendSuccess(_("PIN removed"));
|
|
|
|
} else {
|
|
|
|
fsm_sendSuccess(_("PIN changed"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
layoutHome();
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgWipeDevice(const 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;
|
|
|
|
}
|
|
|
|
config_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();
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgGetEntropy(const GetEntropy *msg) {
|
2018-05-03 14:47:37 +00:00
|
|
|
#if !DEBUG_RNG
|
2019-03-29 16:10:31 +00:00
|
|
|
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;
|
|
|
|
}
|
2018-05-03 14:47:37 +00:00
|
|
|
#endif
|
2019-03-29 16:10:31 +00:00
|
|
|
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();
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgLoadDevice(const LoadDevice *msg) {
|
|
|
|
CHECK_PIN
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
config_loadDevice(msg);
|
|
|
|
fsm_sendSuccess(_("Device loaded"));
|
|
|
|
layoutHome();
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgResetDevice(const ResetDevice *msg) {
|
|
|
|
CHECK_PIN
|
|
|
|
|
|
|
|
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,
|
|
|
|
msg->has_no_backup ? msg->no_backup : false);
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgEntropyAck(const EntropyAck *msg) {
|
|
|
|
if (msg->has_entropy) {
|
|
|
|
reset_entropy(msg->entropy.bytes, msg->entropy.size);
|
|
|
|
} else {
|
|
|
|
reset_entropy(0, 0);
|
|
|
|
}
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgBackupDevice(const BackupDevice *msg) {
|
2019-04-18 14:27:27 +00:00
|
|
|
(void)msg;
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
CHECK_INITIALIZED
|
2018-05-03 14:47:37 +00:00
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
CHECK_PIN_UNCACHED
|
2018-05-03 14:47:37 +00:00
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
char mnemonic[MAX_MNEMONIC_LEN + 1];
|
|
|
|
if (config_getMnemonic(mnemonic, sizeof(mnemonic))) {
|
|
|
|
reset_backup(true, mnemonic);
|
|
|
|
}
|
|
|
|
memzero(mnemonic, sizeof(mnemonic));
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgCancel(const Cancel *msg) {
|
|
|
|
(void)msg;
|
|
|
|
recovery_abort();
|
|
|
|
signing_abort();
|
2019-04-27 14:15:35 +00:00
|
|
|
#if !BITCOIN_ONLY
|
2019-03-29 16:10:31 +00:00
|
|
|
ethereum_signing_abort();
|
2019-04-27 14:15:35 +00:00
|
|
|
#endif
|
2019-03-29 16:10:31 +00:00
|
|
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgClearSession(const ClearSession *msg) {
|
|
|
|
(void)msg;
|
|
|
|
session_clear(true); // clear PIN as well
|
|
|
|
layoutScreensaver();
|
|
|
|
fsm_sendSuccess(_("Session cleared"));
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgApplySettings(const 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"),
|
|
|
|
_("protection?"), 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) {
|
|
|
|
config_setLabel(msg->label);
|
|
|
|
}
|
|
|
|
if (msg->has_language) {
|
|
|
|
config_setLanguage(msg->language);
|
|
|
|
}
|
|
|
|
if (msg->has_use_passphrase) {
|
|
|
|
config_setPassphraseProtection(msg->use_passphrase);
|
|
|
|
}
|
|
|
|
if (msg->has_homescreen) {
|
|
|
|
config_setHomescreen(msg->homescreen.bytes, msg->homescreen.size);
|
|
|
|
}
|
|
|
|
if (msg->has_auto_lock_delay_ms) {
|
|
|
|
config_setAutoLockDelayMs(msg->auto_lock_delay_ms);
|
|
|
|
}
|
|
|
|
fsm_sendSuccess(_("Settings applied"));
|
|
|
|
layoutHome();
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgApplyFlags(const ApplyFlags *msg) {
|
|
|
|
CHECK_PIN
|
2018-05-03 14:47:37 +00:00
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
if (msg->has_flags) {
|
|
|
|
config_applyFlags(msg->flags);
|
|
|
|
}
|
|
|
|
fsm_sendSuccess(_("Flags applied"));
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgRecoveryDevice(const RecoveryDevice *msg) {
|
|
|
|
CHECK_PIN_UNCACHED
|
|
|
|
|
|
|
|
const bool dry_run = msg->has_dry_run ? msg->dry_run : false;
|
|
|
|
if (!dry_run) {
|
|
|
|
CHECK_NOT_INITIALIZED
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK_PARAM(!msg->has_word_count || msg->word_count == 12 ||
|
|
|
|
msg->word_count == 18 || msg->word_count == 24,
|
|
|
|
_("Invalid word count"));
|
|
|
|
|
|
|
|
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);
|
2018-05-03 14:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_msgWordAck(const WordAck *msg) { recovery_word(msg->word); }
|
|
|
|
|
|
|
|
void fsm_msgSetU2FCounter(const 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;
|
|
|
|
}
|
|
|
|
config_setU2FCounter(msg->u2f_counter);
|
|
|
|
fsm_sendSuccess(_("U2F counter set"));
|
|
|
|
layoutHome();
|
2018-05-11 12:03:28 +00:00
|
|
|
}
|