diff --git a/legacy/firmware/config.c b/legacy/firmware/config.c index daba0e222..2691bd033 100644 --- a/legacy/firmware/config.c +++ b/legacy/firmware/config.c @@ -81,6 +81,8 @@ static const uint32_t META_MAGIC_V10 = 0xFFFFFFFF; #define KEY_U2F_ROOT (17 | APP | FLAG_PUBLIC_SHIFTED) // node #define KEY_DEBUG_LINK_PIN (255 | APP | FLAG_PUBLIC_SHIFTED) // string(10) +#define MAX_SESSIONS_COUNT 10 + // The PIN value corresponding to an empty PIN. static const uint32_t PIN_EMPTY = 1; @@ -120,11 +122,23 @@ be added to the storage u2f_counter to get the real counter value. * storage.u2f_counter + config_u2f_offset. * This corresponds to the number of cleared bits in the U2FAREA. */ -static secbool sessionSeedCached; -static uint8_t CONFIDENTIAL sessionSeed[64]; -static secbool sessionIdCached; -static uint8_t sessionId[32]; +// Session management +typedef struct { + uint8_t id[32]; + uint32_t last_use; + uint8_t seed[64]; + secbool seedCached; +} Session; + +static void session_clearCache(Session *session); +static uint8_t session_findLeastRecent(void); +static uint8_t session_findSession(const uint8_t *sessionId); + +static CONFIDENTIAL Session sessionsCache[MAX_SESSIONS_COUNT]; +static Session *activeSessionCache; + +static uint32_t sessionUseCounter = 0; #define autoLockDelayMsDefault (10 * 60 * 1000U) // 10 minutes static secbool autoLockDelayMsCached = secfalse; @@ -402,19 +416,30 @@ void config_init(void) { } data2hex(config_uuid, sizeof(config_uuid), config_uuid_str); + session_clear(false); + usbTiny(oldTiny); } void session_clear(bool lock) { - sessionSeedCached = secfalse; - memzero(&sessionSeed, sizeof(sessionSeed)); - sessionIdCached = secfalse; - memzero(&sessionId, sizeof(sessionId)); + for (uint8_t i = 0; i < MAX_SESSIONS_COUNT; i++) { + session_clearCache(sessionsCache + i); + } + activeSessionCache = NULL; if (lock) { - storage_lock(); + config_lockDevice(); } } +void session_clearCache(Session *session) { + session->last_use = 0; + memzero(session->id, sizeof(session->id)); + memzero(session->seed, sizeof(session->seed)); + session->seedCached = false; +} + +void config_lockDevice(void) { storage_lock(); } + static void get_u2froot_callback(uint32_t iter, uint32_t total) { layoutProgress(_("Updating"), 1000 * iter / total); } @@ -422,11 +447,11 @@ static void get_u2froot_callback(uint32_t iter, uint32_t total) { static void config_compute_u2froot(const char *mnemonic, StorageHDNode *u2froot) { static CONFIDENTIAL HDNode node; + static CONFIDENTIAL uint8_t seed[64]; char oldTiny = usbTiny(1); - mnemonic_to_seed(mnemonic, "", sessionSeed, - get_u2froot_callback); // BIP-0039 + mnemonic_to_seed(mnemonic, "", seed, get_u2froot_callback); // BIP-0039 usbTiny(oldTiny); - hdnode_from_seed(sessionSeed, 64, NIST256P1_NAME, &node); + hdnode_from_seed(seed, 64, NIST256P1_NAME, &node); hdnode_private_ckd(&node, U2F_KEY_PATH); u2froot->depth = node.depth; u2froot->child_num = U2F_KEY_PATH; @@ -437,6 +462,7 @@ static void config_compute_u2froot(const char *mnemonic, memcpy(u2froot->private_key.bytes, node.private_key, sizeof(node.private_key)); memzero(&node, sizeof(node)); + memzero(&seed, sizeof(seed)); session_clear(false); // invalidate seed cache } @@ -551,8 +577,9 @@ static void get_root_node_callback(uint32_t iter, uint32_t total) { const uint8_t *config_getSeed(void) { // root node is properly cached - if (sectrue == sessionSeedCached) { - return sessionSeed; + if ((activeSessionCache != NULL) && + (activeSessionCache->seedCached == sectrue)) { + return activeSessionCache->seed; } // if storage has mnemonic, convert it to node and use it @@ -575,13 +602,17 @@ const uint8_t *config_getSeed(void) { } } char oldTiny = usbTiny(1); - mnemonic_to_seed(mnemonic, passphrase, sessionSeed, + if (activeSessionCache == NULL) { + // this should not happen if the Host behaves and sends Initialize first + session_startSession(NULL); + } + mnemonic_to_seed(mnemonic, passphrase, activeSessionCache->seed, get_root_node_callback); // BIP-0039 memzero(mnemonic, sizeof(mnemonic)); memzero(passphrase, sizeof(passphrase)); usbTiny(oldTiny); - sessionSeedCached = sectrue; - return sessionSeed; + activeSessionCache->seedCached = sectrue; + return activeSessionCache->seed; } else { fsm_sendFailure(FailureType_Failure_NotInitialized, _("Device not initialized")); @@ -772,12 +803,51 @@ bool config_changeWipeCode(const char *pin, const char *wipe_code) { return sectrue == ret; } -const uint8_t *session_getSessionId(void) { - if (!sessionIdCached) { - random_buffer(sessionId, 32); +uint8_t session_findLeastRecent(void) { + uint8_t least_recent_index = MAX_SESSIONS_COUNT; + uint32_t least_recent_use = sessionUseCounter; + for (uint8_t i = 0; i < MAX_SESSIONS_COUNT; i++) { + if (sessionsCache[i].last_use == 0) { + return i; + } + if (sessionsCache[i].last_use <= least_recent_use) { + least_recent_use = sessionsCache[i].last_use; + least_recent_index = i; + } } - sessionIdCached = sectrue; - return sessionId; + ensure(sectrue * (least_recent_index < MAX_SESSIONS_COUNT), NULL); + return least_recent_index; +} + +uint8_t session_findSession(const uint8_t *sessionId) { + for (uint8_t i = 0; i < MAX_SESSIONS_COUNT; i++) { + if (sessionsCache[i].last_use != 0) { + if (memcmp(sessionsCache[i].id, sessionId, 32) == 0) { // session found + return i; + } + } + } + return MAX_SESSIONS_COUNT; +} + +uint8_t *session_startSession(const uint8_t *received_session_id) { + int session_index = MAX_SESSIONS_COUNT; + + if (received_session_id != NULL) { + session_index = session_findSession(received_session_id); + } + + if (session_index == MAX_SESSIONS_COUNT) { + // Session not found in cache. Use an empty one or the least recently used. + session_index = session_findLeastRecent(); + session_clearCache(sessionsCache + session_index); + random_buffer(sessionsCache[session_index].id, 32); + } + + sessionUseCounter++; + sessionsCache[session_index].last_use = sessionUseCounter; + activeSessionCache = sessionsCache + session_index; + return activeSessionCache->id; } bool session_isUnlocked(void) { return sectrue == storage_is_unlocked(); } diff --git a/legacy/firmware/config.h b/legacy/firmware/config.h index 41464cab1..6f190db89 100644 --- a/legacy/firmware/config.h +++ b/legacy/firmware/config.h @@ -88,6 +88,7 @@ extern Storage configUpdate; void config_init(void); void session_clear(bool lock); +void config_lockDevice(void); void config_loadDevice(const LoadDevice *msg); @@ -108,7 +109,7 @@ bool config_getPassphraseProtection(bool *passphrase_protection); bool config_getHomescreen(uint8_t *dest, uint16_t dest_size); void config_setHomescreen(const uint8_t *data, uint32_t size); -const uint8_t *session_getSessionId(void); +uint8_t *session_startSession(const uint8_t *received_session_id); bool config_setMnemonic(const char *mnemonic); bool config_containsMnemonic(const char *mnemonic); diff --git a/legacy/firmware/fsm_msg_common.h b/legacy/firmware/fsm_msg_common.h index a8834430e..0b8ba8da9 100644 --- a/legacy/firmware/fsm_msg_common.h +++ b/legacy/firmware/fsm_msg_common.h @@ -17,31 +17,7 @@ * along with this library. If not, see . */ -void fsm_msgInitialize(const Initialize *msg) { - recovery_abort(); - signing_abort(); - if (msg && msg->has_session_id && msg->session_id.size == 32) { - if (0 != memcmp(session_getSessionId(), msg->session_id.bytes, 32)) { - // If session id was specified but does not match -> clear the cache. - session_clear(false); // do not lock - } - } else { - // If session id was not specified -> clear the cache. - session_clear(false); // do not lock - } - layoutHome(); - fsm_msgGetFeatures(0); -} - -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); - +bool get_features(Features *resp) { resp->has_vendor = true; strlcpy(resp->vendor, "trezor.io", sizeof(resp->vendor)); resp->has_major_version = true; @@ -103,7 +79,35 @@ void fsm_msgGetFeatures(const GetFeatures *msg) { resp->capabilities[6] = Capability_Capability_Stellar; resp->capabilities[7] = Capability_Capability_U2F; #endif + return resp; +} + +void fsm_msgInitialize(const Initialize *msg) { + recovery_abort(); + signing_abort(); + + uint8_t *session_id; + if (msg && msg->has_session_id) { + session_id = session_startSession(msg->session_id.bytes); + } else { + session_id = session_startSession(NULL); + } + + RESP_INIT(Features); + get_features(resp); + + resp->has_session_id = true; + memcpy(resp->session_id.bytes, session_id, sizeof(resp->session_id.bytes)); + resp->session_id.size = sizeof(resp->session_id.bytes); + layoutHome(); + msg_write(MessageType_MessageType_Features, resp); +} + +void fsm_msgGetFeatures(const GetFeatures *msg) { + (void)msg; + RESP_INIT(Features); + get_features(resp); msg_write(MessageType_MessageType_Features, resp); } @@ -343,7 +347,9 @@ void fsm_msgCancel(const Cancel *msg) { void fsm_msgClearSession(const ClearSession *msg) { (void)msg; - session_clear(true); // clear PIN as well + // we do not actually clear the session, we just lock it + // TODO: the message should be called LockSession see #819 + config_lockDevice(); layoutScreensaver(); fsm_sendSuccess(_("Session cleared")); } diff --git a/legacy/firmware/trezor.c b/legacy/firmware/trezor.c index 3cbb1454f..22c23cac6 100644 --- a/legacy/firmware/trezor.c +++ b/legacy/firmware/trezor.c @@ -72,7 +72,7 @@ void check_lock_screen(void) { if (button.YesUp) { // lock the screen - session_clear(true); + config_lockDevice(); layoutScreensaver(); } else { // resume homescreen @@ -85,7 +85,7 @@ void check_lock_screen(void) { if ((timer_ms() - system_millis_lock_start) >= config_getAutoLockDelayMs()) { // lock the screen - session_clear(true); + config_lockDevice(); layoutScreensaver(); } }