mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-14 17:31:04 +00:00
legacy: new passphrase redesign draft
This commit is contained in:
parent
569068044e
commit
041a95f738
@ -122,6 +122,9 @@ be added to the storage u2f_counter to get the real counter value.
|
|||||||
static secbool sessionSeedCached, sessionSeedUsesPassphrase;
|
static secbool sessionSeedCached, sessionSeedUsesPassphrase;
|
||||||
static uint8_t CONFIDENTIAL sessionSeed[64];
|
static uint8_t CONFIDENTIAL sessionSeed[64];
|
||||||
|
|
||||||
|
static secbool sessionIdCached;
|
||||||
|
static uint8_t sessionId[32];
|
||||||
|
|
||||||
static secbool sessionPassphraseCached = secfalse;
|
static secbool sessionPassphraseCached = secfalse;
|
||||||
static char CONFIDENTIAL sessionPassphrase[51];
|
static char CONFIDENTIAL sessionPassphrase[51];
|
||||||
|
|
||||||
@ -409,6 +412,8 @@ void session_clear(bool lock) {
|
|||||||
memzero(&sessionSeed, sizeof(sessionSeed));
|
memzero(&sessionSeed, sizeof(sessionSeed));
|
||||||
sessionPassphraseCached = secfalse;
|
sessionPassphraseCached = secfalse;
|
||||||
memzero(&sessionPassphrase, sizeof(sessionPassphrase));
|
memzero(&sessionPassphrase, sizeof(sessionPassphrase));
|
||||||
|
sessionIdCached = secfalse;
|
||||||
|
memzero(&sessionId, sizeof(sessionId));
|
||||||
if (lock) {
|
if (lock) {
|
||||||
storage_lock();
|
storage_lock();
|
||||||
}
|
}
|
||||||
@ -527,8 +532,6 @@ void config_setLanguage(const char *lang) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void config_setPassphraseProtection(bool passphrase_protection) {
|
void config_setPassphraseProtection(bool passphrase_protection) {
|
||||||
sessionSeedCached = secfalse;
|
|
||||||
sessionPassphraseCached = secfalse;
|
|
||||||
config_set_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection);
|
config_set_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,6 +555,7 @@ static void get_root_node_callback(uint32_t iter, uint32_t total) {
|
|||||||
|
|
||||||
const uint8_t *config_getSeed(bool usePassphrase) {
|
const uint8_t *config_getSeed(bool usePassphrase) {
|
||||||
// root node is properly cached
|
// root node is properly cached
|
||||||
|
// TODO: investigate
|
||||||
if (usePassphrase == (sectrue == sessionSeedUsesPassphrase) &&
|
if (usePassphrase == (sectrue == sessionSeedUsesPassphrase) &&
|
||||||
sectrue == sessionSeedCached) {
|
sectrue == sessionSeedCached) {
|
||||||
return sessionSeed;
|
return sessionSeed;
|
||||||
@ -820,31 +824,12 @@ bool session_isPassphraseCached(void) {
|
|||||||
return sectrue == sessionPassphraseCached;
|
return sectrue == sessionPassphraseCached;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool session_getState(const uint8_t *salt, uint8_t *state,
|
const uint8_t *session_getSessionId(void) {
|
||||||
const char *passphrase) {
|
if (!sessionIdCached) {
|
||||||
if (!passphrase && sectrue != sessionPassphraseCached) {
|
random_buffer(sessionId, 32);
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
passphrase = sessionPassphrase;
|
|
||||||
}
|
}
|
||||||
if (!salt) {
|
sessionIdCached = sectrue;
|
||||||
// if salt is not provided fill the first half of the state with random data
|
return sessionId;
|
||||||
random_buffer(state, 32);
|
|
||||||
} else {
|
|
||||||
// if salt is provided fill the first half of the state with salt
|
|
||||||
memcpy(state, salt, 32);
|
|
||||||
}
|
|
||||||
// state[0:32] = salt
|
|
||||||
// state[32:64] = HMAC(passphrase, salt || device_id)
|
|
||||||
HMAC_SHA256_CTX ctx = {0};
|
|
||||||
hmac_sha256_Init(&ctx, (const uint8_t *)passphrase, strlen(passphrase));
|
|
||||||
hmac_sha256_Update(&ctx, state, 32);
|
|
||||||
hmac_sha256_Update(&ctx, (const uint8_t *)config_uuid, sizeof(config_uuid));
|
|
||||||
hmac_sha256_Final(&ctx, state + 32);
|
|
||||||
|
|
||||||
memzero(&ctx, sizeof(ctx));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool session_isUnlocked(void) { return sectrue == storage_is_unlocked(); }
|
bool session_isUnlocked(void) { return sectrue == storage_is_unlocked(); }
|
||||||
|
@ -110,8 +110,7 @@ void config_setHomescreen(const uint8_t *data, uint32_t size);
|
|||||||
|
|
||||||
void session_cachePassphrase(const char *passphrase);
|
void session_cachePassphrase(const char *passphrase);
|
||||||
bool session_isPassphraseCached(void);
|
bool session_isPassphraseCached(void);
|
||||||
bool session_getState(const uint8_t *salt, uint8_t *state,
|
const uint8_t *session_getSessionId(void);
|
||||||
const char *passphrase);
|
|
||||||
|
|
||||||
bool config_setMnemonic(const char *mnemonic);
|
bool config_setMnemonic(const char *mnemonic);
|
||||||
bool config_containsMnemonic(const char *mnemonic);
|
bool config_containsMnemonic(const char *mnemonic);
|
||||||
|
@ -20,17 +20,13 @@
|
|||||||
void fsm_msgInitialize(const Initialize *msg) {
|
void fsm_msgInitialize(const Initialize *msg) {
|
||||||
recovery_abort();
|
recovery_abort();
|
||||||
signing_abort();
|
signing_abort();
|
||||||
if (msg && msg->has_state && msg->state.size == 64) {
|
if (msg && msg->has_session_id && msg->session_id.size == 32) {
|
||||||
uint8_t i_state[64];
|
if (0 != memcmp(session_getSessionId(), msg->session_id.bytes, 32)) {
|
||||||
if (!session_getState(msg->state.bytes, i_state, NULL)) {
|
session_clear(false); // do not lock
|
||||||
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 {
|
} else {
|
||||||
session_clear(false); // do not clear PIN
|
// If session id was not specified -> clear the cache.
|
||||||
|
session_clear(false); // do not lock
|
||||||
}
|
}
|
||||||
layoutHome();
|
layoutHome();
|
||||||
fsm_msgGetFeatures(0);
|
fsm_msgGetFeatures(0);
|
||||||
@ -39,6 +35,12 @@ void fsm_msgInitialize(const Initialize *msg) {
|
|||||||
void fsm_msgGetFeatures(const GetFeatures *msg) {
|
void fsm_msgGetFeatures(const GetFeatures *msg) {
|
||||||
(void)msg;
|
(void)msg;
|
||||||
RESP_INIT(Features);
|
RESP_INIT(Features);
|
||||||
|
|
||||||
|
resp->has_session_id = true;
|
||||||
|
memcpy(resp->session_id.bytes, session_getSessionId(),
|
||||||
|
sizeof(resp->session_id.bytes));
|
||||||
|
resp->session_id.size = sizeof(resp->session_id.bytes);
|
||||||
|
|
||||||
resp->has_vendor = true;
|
resp->has_vendor = true;
|
||||||
strlcpy(resp->vendor, "trezor.io", sizeof(resp->vendor));
|
strlcpy(resp->vendor, "trezor.io", sizeof(resp->vendor));
|
||||||
resp->has_major_version = true;
|
resp->has_major_version = true;
|
||||||
@ -359,6 +361,10 @@ void fsm_msgClearSession(const ClearSession *msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fsm_msgApplySettings(const ApplySettings *msg) {
|
void fsm_msgApplySettings(const ApplySettings *msg) {
|
||||||
|
CHECK_PARAM(
|
||||||
|
!msg->has_passphrase_always_on_device,
|
||||||
|
_("This firmware is incapable of passphrase entry on the device."));
|
||||||
|
|
||||||
CHECK_PARAM(msg->has_label || msg->has_language || msg->has_use_passphrase ||
|
CHECK_PARAM(msg->has_label || msg->has_language || msg->has_use_passphrase ||
|
||||||
msg->has_homescreen || msg->has_auto_lock_delay_ms,
|
msg->has_homescreen || msg->has_auto_lock_delay_ms,
|
||||||
_("No setting provided"));
|
_("No setting provided"));
|
||||||
|
@ -354,6 +354,7 @@ bool protectPassphrase(void) {
|
|||||||
bool passphrase_protection = false;
|
bool passphrase_protection = false;
|
||||||
config_getPassphraseProtection(&passphrase_protection);
|
config_getPassphraseProtection(&passphrase_protection);
|
||||||
if (!passphrase_protection || session_isPassphraseCached()) {
|
if (!passphrase_protection || session_isPassphraseCached()) {
|
||||||
|
session_cachePassphrase("");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,11 +370,15 @@ bool protectPassphrase(void) {
|
|||||||
bool result;
|
bool result;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
usbPoll();
|
usbPoll();
|
||||||
// TODO: correctly process PassphraseAck with state field set (mismatch =>
|
|
||||||
// Failure)
|
|
||||||
if (msg_tiny_id == MessageType_MessageType_PassphraseAck) {
|
if (msg_tiny_id == MessageType_MessageType_PassphraseAck) {
|
||||||
msg_tiny_id = 0xFFFF;
|
msg_tiny_id = 0xFFFF;
|
||||||
PassphraseAck *ppa = (PassphraseAck *)msg_tiny;
|
PassphraseAck *ppa = (PassphraseAck *)msg_tiny;
|
||||||
|
if (ppa->has_on_device && ppa->on_device == true) {
|
||||||
|
fsm_sendFailure(
|
||||||
|
FailureType_Failure_DataError,
|
||||||
|
_("This firmware is incapable of passphrase entry on the device."));
|
||||||
|
// TODO: write test
|
||||||
|
}
|
||||||
session_cachePassphrase(ppa->has_passphrase ? ppa->passphrase : "");
|
session_cachePassphrase(ppa->has_passphrase ? ppa->passphrase : "");
|
||||||
result = true;
|
result = true;
|
||||||
break;
|
break;
|
||||||
|
@ -5,9 +5,6 @@ Failure.message max_size:256
|
|||||||
PinMatrixAck.pin max_size:10
|
PinMatrixAck.pin max_size:10
|
||||||
|
|
||||||
PassphraseAck.passphrase max_size:51
|
PassphraseAck.passphrase max_size:51
|
||||||
PassphraseAck.state max_size:64
|
|
||||||
|
|
||||||
PassphraseStateRequest.state max_size:64
|
|
||||||
|
|
||||||
HDNodeType.chain_code max_size:32
|
HDNodeType.chain_code max_size:32
|
||||||
HDNodeType.private_key max_size:32
|
HDNodeType.private_key max_size:32
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Initialize.state max_size:64
|
Initialize.session_id max_size:32
|
||||||
|
|
||||||
Features.vendor max_size:33
|
Features.vendor max_size:33
|
||||||
Features.device_id max_size:25
|
Features.device_id max_size:25
|
||||||
@ -10,6 +10,7 @@ Features.model max_size:17
|
|||||||
Features.fw_vendor max_size:256
|
Features.fw_vendor max_size:256
|
||||||
Features.fw_vendor_keys max_size:32
|
Features.fw_vendor_keys max_size:32
|
||||||
Features.capabilities max_count:32
|
Features.capabilities max_count:32
|
||||||
|
Features.session_id max_size:32
|
||||||
|
|
||||||
ApplySettings.language max_size:17
|
ApplySettings.language max_size:17
|
||||||
ApplySettings.label max_size:33
|
ApplySettings.label max_size:33
|
||||||
|
@ -39,7 +39,6 @@ def _enable_passphrase(client):
|
|||||||
assert isinstance(response, messages.Success)
|
assert isinstance(response, messages.Success)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip_t1 # TODO
|
|
||||||
@pytest.mark.skip_ui
|
@pytest.mark.skip_ui
|
||||||
@pytest.mark.setup_client()
|
@pytest.mark.setup_client()
|
||||||
def test_session_with_passphrase(client):
|
def test_session_with_passphrase(client):
|
||||||
@ -91,7 +90,6 @@ def test_session_with_passphrase(client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip_t1 # TODO
|
|
||||||
@pytest.mark.skip_ui
|
@pytest.mark.skip_ui
|
||||||
@pytest.mark.setup_client()
|
@pytest.mark.setup_client()
|
||||||
def test_session_enable_passphrase(client):
|
def test_session_enable_passphrase(client):
|
||||||
@ -132,7 +130,6 @@ def test_session_enable_passphrase(client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip_t1
|
|
||||||
@pytest.mark.skip_ui
|
@pytest.mark.skip_ui
|
||||||
@pytest.mark.setup_client()
|
@pytest.mark.setup_client()
|
||||||
def test_passphrase_always_on_device(client):
|
def test_passphrase_always_on_device(client):
|
||||||
@ -146,6 +143,12 @@ def test_passphrase_always_on_device(client):
|
|||||||
|
|
||||||
# Force passphrase entry on Trezor.
|
# Force passphrase entry on Trezor.
|
||||||
response = client.call_raw(messages.ApplySettings(passphrase_always_on_device=True))
|
response = client.call_raw(messages.ApplySettings(passphrase_always_on_device=True))
|
||||||
|
|
||||||
|
if client.features.model == "1":
|
||||||
|
assert isinstance(response, messages.Failure)
|
||||||
|
assert response.code == 3 # DataError
|
||||||
|
return
|
||||||
|
|
||||||
assert isinstance(response, messages.ButtonRequest) # confirm dialog
|
assert isinstance(response, messages.ButtonRequest) # confirm dialog
|
||||||
client.debug.press_yes()
|
client.debug.press_yes()
|
||||||
response = client.call_raw(messages.ButtonAck())
|
response = client.call_raw(messages.ButtonAck())
|
||||||
|
Loading…
Reference in New Issue
Block a user