legacy: new passphrase redesign draft

pull/803/head
Tomas Susanka 4 years ago committed by Pavol Rusnak
parent 569068044e
commit 041a95f738
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D

@ -122,6 +122,9 @@ be added to the storage u2f_counter to get the real counter value.
static secbool sessionSeedCached, sessionSeedUsesPassphrase;
static uint8_t CONFIDENTIAL sessionSeed[64];
static secbool sessionIdCached;
static uint8_t sessionId[32];
static secbool sessionPassphraseCached = secfalse;
static char CONFIDENTIAL sessionPassphrase[51];
@ -409,6 +412,8 @@ void session_clear(bool lock) {
memzero(&sessionSeed, sizeof(sessionSeed));
sessionPassphraseCached = secfalse;
memzero(&sessionPassphrase, sizeof(sessionPassphrase));
sessionIdCached = secfalse;
memzero(&sessionId, sizeof(sessionId));
if (lock) {
storage_lock();
}
@ -527,8 +532,6 @@ void config_setLanguage(const char *lang) {
}
void config_setPassphraseProtection(bool passphrase_protection) {
sessionSeedCached = secfalse;
sessionPassphraseCached = secfalse;
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) {
// root node is properly cached
// TODO: investigate
if (usePassphrase == (sectrue == sessionSeedUsesPassphrase) &&
sectrue == sessionSeedCached) {
return sessionSeed;
@ -820,31 +824,12 @@ bool session_isPassphraseCached(void) {
return sectrue == sessionPassphraseCached;
}
bool session_getState(const uint8_t *salt, uint8_t *state,
const char *passphrase) {
if (!passphrase && sectrue != sessionPassphraseCached) {
return false;
} else {
passphrase = sessionPassphrase;
const uint8_t *session_getSessionId(void) {
if (!sessionIdCached) {
random_buffer(sessionId, 32);
}
if (!salt) {
// if salt is not provided fill the first half of the state with random data
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;
sessionIdCached = sectrue;
return sessionId;
}
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);
bool session_isPassphraseCached(void);
bool session_getState(const uint8_t *salt, uint8_t *state,
const char *passphrase);
const uint8_t *session_getSessionId(void);
bool config_setMnemonic(const char *mnemonic);
bool config_containsMnemonic(const char *mnemonic);

@ -20,17 +20,13 @@
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
}
if (msg && msg->has_session_id && msg->session_id.size == 32) {
if (0 != memcmp(session_getSessionId(), msg->session_id.bytes, 32)) {
session_clear(false); // do not lock
}
} else {
session_clear(false); // do not clear PIN
// If session id was not specified -> clear the cache.
session_clear(false); // do not lock
}
layoutHome();
fsm_msgGetFeatures(0);
@ -39,6 +35,12 @@ void fsm_msgInitialize(const Initialize *msg) {
void fsm_msgGetFeatures(const GetFeatures *msg) {
(void)msg;
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;
strlcpy(resp->vendor, "trezor.io", sizeof(resp->vendor));
resp->has_major_version = true;
@ -359,6 +361,10 @@ void fsm_msgClearSession(const ClearSession *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 ||
msg->has_homescreen || msg->has_auto_lock_delay_ms,
_("No setting provided"));

@ -354,6 +354,7 @@ bool protectPassphrase(void) {
bool passphrase_protection = false;
config_getPassphraseProtection(&passphrase_protection);
if (!passphrase_protection || session_isPassphraseCached()) {
session_cachePassphrase("");
return true;
}
@ -369,11 +370,15 @@ bool protectPassphrase(void) {
bool result;
for (;;) {
usbPoll();
// TODO: correctly process PassphraseAck with state field set (mismatch =>
// Failure)
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) {
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 : "");
result = true;
break;

@ -5,9 +5,6 @@ Failure.message max_size:256
PinMatrixAck.pin max_size:10
PassphraseAck.passphrase max_size:51
PassphraseAck.state max_size:64
PassphraseStateRequest.state max_size:64
HDNodeType.chain_code 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.device_id max_size:25
@ -10,6 +10,7 @@ Features.model max_size:17
Features.fw_vendor max_size:256
Features.fw_vendor_keys max_size:32
Features.capabilities max_count:32
Features.session_id max_size:32
ApplySettings.language max_size:17
ApplySettings.label max_size:33

@ -39,7 +39,6 @@ def _enable_passphrase(client):
assert isinstance(response, messages.Success)
@pytest.mark.skip_t1 # TODO
@pytest.mark.skip_ui
@pytest.mark.setup_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.setup_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.setup_client()
def test_passphrase_always_on_device(client):
@ -146,6 +143,12 @@ def test_passphrase_always_on_device(client):
# Force passphrase entry on Trezor.
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
client.debug.press_yes()
response = client.call_raw(messages.ButtonAck())

Loading…
Cancel
Save