You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/legacy/firmware/protect.c

455 lines
14 KiB

/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2014 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/>.
*/
#include "protect.h"
#include "buttons.h"
#include "config.h"
#include "debug.h"
#include "fsm.h"
#include "gettext.h"
#include "keyboard.h"
#include "layout2.h"
#include "memory.h"
#include "memzero.h"
#include "messages.h"
#include "messages.pb.h"
#include "oled.h"
#include "pinmatrix.h"
#include "usb.h"
#include "util.h"
bool protectAbortedByCancel = false;
bool protectAbortedByInitialize = false;
bool protectButton(ButtonRequestType type, bool confirm_only) {
ButtonRequest resp = {0};
bool result = false;
bool acked = false;
#if DEBUG_LINK
bool debug_decided = false;
#endif
memzero(&resp, sizeof(ButtonRequest));
resp.has_code = true;
resp.code = type;
usbTiny(1);
buttonUpdate(); // Clear button state
msg_write(MessageType_MessageType_ButtonRequest, &resp);
for (;;) {
usbPoll();
// check for ButtonAck
if (msg_tiny_id == MessageType_MessageType_ButtonAck) {
msg_tiny_id = 0xFFFF;
acked = true;
}
// button acked - check buttons
if (acked) {
usbSleep(5);
buttonUpdate();
if (button.YesReleased) {
result = true;
break;
}
if (!confirm_only && button.NoReleased) {
result = false;
break;
}
}
// check for Cancel / Initialize
protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel);
protectAbortedByInitialize =
(msg_tiny_id == MessageType_MessageType_Initialize);
if (protectAbortedByCancel || protectAbortedByInitialize) {
msg_tiny_id = 0xFFFF;
result = false;
break;
}
#if DEBUG_LINK
// check DebugLink
if (msg_tiny_id == MessageType_MessageType_DebugLinkDecision) {
msg_tiny_id = 0xFFFF;
DebugLinkDecision *dld = (DebugLinkDecision *)msg_tiny;
result = dld->yes_no;
debug_decided = true;
}
if (acked && debug_decided) {
break;
}
if (msg_tiny_id == MessageType_MessageType_DebugLinkGetState) {
msg_tiny_id = 0xFFFF;
fsm_msgDebugLinkGetState((DebugLinkGetState *)msg_tiny);
}
#endif
}
usbTiny(0);
return result;
}
const char *requestPin(PinMatrixRequestType type, const char *text) {
return pin_keyboard(text);
PinMatrixRequest resp = {0};
memzero(&resp, sizeof(PinMatrixRequest));
resp.has_type = true;
resp.type = type;
usbTiny(1);
msg_write(MessageType_MessageType_PinMatrixRequest, &resp);
pinmatrix_start(text);
for (;;) {
usbPoll();
if (msg_tiny_id == MessageType_MessageType_PinMatrixAck) {
msg_tiny_id = 0xFFFF;
PinMatrixAck *pma = (PinMatrixAck *)msg_tiny;
usbTiny(0);
if (sectrue == pinmatrix_done(pma->pin)) // convert via pinmatrix
return pma->pin;
else
return 0;
}
// check for Cancel / Initialize
protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel);
protectAbortedByInitialize =
(msg_tiny_id == MessageType_MessageType_Initialize);
if (protectAbortedByCancel || protectAbortedByInitialize) {
pinmatrix_done(0);
msg_tiny_id = 0xFFFF;
usbTiny(0);
return 0;
}
#if DEBUG_LINK
if (msg_tiny_id == MessageType_MessageType_DebugLinkGetState) {
msg_tiny_id = 0xFFFF;
fsm_msgDebugLinkGetState((DebugLinkGetState *)msg_tiny);
}
#endif
}
}
secbool protectPinUiCallback(uint32_t wait, uint32_t progress,
const char *message) {
// Convert wait to secstr string.
char secstrbuf[] = _("________0 seconds");
char *secstr = secstrbuf + 9;
uint32_t secs = wait;
do {
secstr--;
*secstr = (secs % 10) + '0';
secs /= 10;
} while (secs > 0 && secstr >= secstrbuf);
if (wait == 1) {
// Change "seconds" to "second".
secstrbuf[16] = 0;
}
oledClear();
oledDrawStringCenter(OLED_WIDTH / 2, 0 * 9, message, FONT_STANDARD);
oledDrawStringCenter(OLED_WIDTH / 2, 2 * 9, _("Please wait"), FONT_STANDARD);
oledDrawStringCenter(OLED_WIDTH / 2, 3 * 9, secstr, FONT_STANDARD);
oledDrawStringCenter(OLED_WIDTH / 2, 4 * 9, _("to continue ..."),
FONT_STANDARD);
// progressbar
oledFrame(0, OLED_HEIGHT - 8, OLED_WIDTH - 1, OLED_HEIGHT - 1);
oledBox(1, OLED_HEIGHT - 7, OLED_WIDTH - 2, OLED_HEIGHT - 2, 0);
progress = progress * (OLED_WIDTH - 4) / 1000;
if (progress > OLED_WIDTH - 4) {
progress = OLED_WIDTH - 4;
}
oledBox(2, OLED_HEIGHT - 6, 1 + progress, OLED_HEIGHT - 3, 1);
oledRefresh();
// Check for Cancel / Initialize.
protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel);
protectAbortedByInitialize =
(msg_tiny_id == MessageType_MessageType_Initialize);
if (protectAbortedByCancel || protectAbortedByInitialize) {
msg_tiny_id = 0xFFFF;
usbTiny(0);
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return sectrue;
}
return secfalse;
}
bool protectPin(bool use_cached) {
if (use_cached && session_isUnlocked()) {
return true;
}
const char *pin = "";
if (config_hasPin()) {
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current,
_("Please enter current PIN:"));
if (!pin) {
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
}
bool ret = config_unlock(pin);
if (!ret) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
}
return ret;
}
bool protectChangePin(bool removal) {
static CONFIDENTIAL char old_pin[MAX_PIN_LEN + 1] = "";
static CONFIDENTIAL char new_pin[MAX_PIN_LEN + 1] = "";
const char *pin = NULL;
if (config_hasPin()) {
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current,
_("Please enter current PIN:"));
if (pin == NULL) {
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
// If removing, defer the check to config_changePin().
if (!removal) {
usbTiny(1);
bool ret = config_unlock(pin);
usbTiny(0);
if (ret == false) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
return false;
}
}
strlcpy(old_pin, pin, sizeof(old_pin));
}
if (!removal) {
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewFirst,
_("Please enter new PIN:"));
if (pin == NULL || pin[0] == '\0') {
memzero(old_pin, sizeof(old_pin));
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
strlcpy(new_pin, pin, sizeof(new_pin));
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewSecond,
_("Please re-enter new PIN:"));
if (pin == NULL) {
memzero(old_pin, sizeof(old_pin));
memzero(new_pin, sizeof(new_pin));
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
if (strncmp(new_pin, pin, sizeof(new_pin)) != 0) {
memzero(old_pin, sizeof(old_pin));
memzero(new_pin, sizeof(new_pin));
fsm_sendFailure(FailureType_Failure_PinMismatch, NULL);
return false;
}
}
bool ret = config_changePin(old_pin, new_pin);
memzero(old_pin, sizeof(old_pin));
memzero(new_pin, sizeof(new_pin));
if (ret == false) {
if (removal) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
} else {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("The new PIN must be different from your wipe code."));
}
}
return ret;
}
bool protectChangeWipeCode(bool removal) {
static CONFIDENTIAL char pin[MAX_PIN_LEN + 1] = "";
static CONFIDENTIAL char wipe_code[MAX_PIN_LEN + 1] = "";
const char *input = NULL;
if (config_hasPin()) {
input = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current,
_("Please enter your PIN:"));
if (input == NULL) {
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
// If removing, defer the check to config_changeWipeCode().
if (!removal) {
usbTiny(1);
bool ret = config_unlock(input);
usbTiny(0);
if (ret == false) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
return false;
}
}
strlcpy(pin, input, sizeof(pin));
}
if (!removal) {
input = requestPin(PinMatrixRequestType_PinMatrixRequestType_WipeCodeFirst,
_("Enter new wipe code:"));
if (input == NULL) {
memzero(pin, sizeof(pin));
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
return false;
}
if (strncmp(pin, input, sizeof(pin)) == 0) {
memzero(pin, sizeof(pin));
fsm_sendFailure(FailureType_Failure_ProcessError,
_("The wipe code must be different from your PIN."));
return false;
}
strlcpy(wipe_code, input, sizeof(wipe_code));
input = requestPin(PinMatrixRequestType_PinMatrixRequestType_WipeCodeSecond,
_("Re-enter new wipe code:"));
if (input == NULL) {
memzero(pin, sizeof(pin));
memzero(wipe_code, sizeof(wipe_code));
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
return false;
}
if (strncmp(wipe_code, input, sizeof(wipe_code)) != 0) {
memzero(pin, sizeof(pin));
memzero(wipe_code, sizeof(wipe_code));
fsm_sendFailure(FailureType_Failure_WipeCodeMismatch, NULL);
return false;
}
}
bool ret = config_changeWipeCode(pin, wipe_code);
memzero(pin, sizeof(pin));
memzero(wipe_code, sizeof(wipe_code));
if (ret == false) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
}
return ret;
}
static bool protectPassphraseOnDevice(char *passphrase) {
const char *input = passphrase_keyboard(_("Please enter passphrase"));
if (input) {
strlcpy(passphrase, input, MAX_PASSPHRASE_LEN);
return true;
} else {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
return false;
}
}
static bool protectPassphraseOnHost(char *passphrase) {
PassphraseRequest resp = {0};
memzero(&resp, sizeof(PassphraseRequest));
usbTiny(1);
msg_write(MessageType_MessageType_PassphraseRequest, &resp);
layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, _("Please enter your"),
_("passphrase using"), _("the computer's"), _("keyboard."),
NULL, NULL);
bool result;
for (;;) {
usbPoll();
if (msg_tiny_id == MessageType_MessageType_PassphraseAck) {
msg_tiny_id = 0xFFFF;
PassphraseAck *ppa = (PassphraseAck *)msg_tiny;
if (ppa->has_on_device && ppa->on_device == true) {
if (ppa->has_passphrase) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Passphrase provided when it should not be"));
result = false;
break;
}
result = protectPassphraseOnDevice(passphrase);
break;
}
if (!ppa->has_passphrase) {
fsm_sendFailure(FailureType_Failure_DataError,
_("No passphrase provided. Use empty string to set an "
"empty passphrase."));
result = false;
break;
}
strlcpy(passphrase, ppa->passphrase, sizeof(ppa->passphrase));
if (passphrase[0] != '\0') {
// passphrase is used - confirm on the display
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Access hidden wallet?"), NULL,
_("Next screen will show"), _("the passphrase!"),
NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Passphrase dismissed"));
result = false;
break;
}
layoutShowPassphrase(passphrase);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Passphrase dismissed"));
result = false;
break;
}
}
result = true;
break;
}
// check for Cancel / Initialize
protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel);
protectAbortedByInitialize =
(msg_tiny_id == MessageType_MessageType_Initialize);
if (protectAbortedByCancel || protectAbortedByInitialize) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
result = false;
msg_tiny_id = 0xFFFF;
break;
}
}
usbTiny(0);
layoutHome();
return result;
}
bool protectPassphrase(char *passphrase) {
memzero(passphrase, MAX_PASSPHRASE_LEN + 1);
bool passphrase_protection = false;
config_getPassphraseProtection(&passphrase_protection);
if (!passphrase_protection) {
// passphrase already set to empty by memzero above
return true;
}
if (config_getPassphraseAlwaysOnDevice()) {
return protectPassphraseOnDevice(passphrase);
} else {
return protectPassphraseOnHost(passphrase);
}
}