legacy: store multiple sessions/caches at the same time

pull/852/head
Tomas Susanka 4 years ago
parent 6c47bf8230
commit f93f6e445b

@ -81,6 +81,8 @@ static const uint32_t META_MAGIC_V10 = 0xFFFFFFFF;
#define KEY_U2F_ROOT (17 | APP | FLAG_PUBLIC_SHIFTED) // node #define KEY_U2F_ROOT (17 | APP | FLAG_PUBLIC_SHIFTED) // node
#define KEY_DEBUG_LINK_PIN (255 | APP | FLAG_PUBLIC_SHIFTED) // string(10) #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. // The PIN value corresponding to an empty PIN.
static const uint32_t PIN_EMPTY = 1; 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. * storage.u2f_counter + config_u2f_offset.
* This corresponds to the number of cleared bits in the U2FAREA. * This corresponds to the number of cleared bits in the U2FAREA.
*/ */
static secbool sessionSeedCached;
static uint8_t CONFIDENTIAL sessionSeed[64];
static secbool sessionIdCached; // Session management
static uint8_t sessionId[32]; 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 #define autoLockDelayMsDefault (10 * 60 * 1000U) // 10 minutes
static secbool autoLockDelayMsCached = secfalse; static secbool autoLockDelayMsCached = secfalse;
@ -402,19 +416,30 @@ void config_init(void) {
} }
data2hex(config_uuid, sizeof(config_uuid), config_uuid_str); data2hex(config_uuid, sizeof(config_uuid), config_uuid_str);
session_clear(false);
usbTiny(oldTiny); usbTiny(oldTiny);
} }
void session_clear(bool lock) { void session_clear(bool lock) {
sessionSeedCached = secfalse; for (uint8_t i = 0; i < MAX_SESSIONS_COUNT; i++) {
memzero(&sessionSeed, sizeof(sessionSeed)); session_clearCache(sessionsCache + i);
sessionIdCached = secfalse; }
memzero(&sessionId, sizeof(sessionId)); activeSessionCache = NULL;
if (lock) { 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) { static void get_u2froot_callback(uint32_t iter, uint32_t total) {
layoutProgress(_("Updating"), 1000 * iter / 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, static void config_compute_u2froot(const char *mnemonic,
StorageHDNode *u2froot) { StorageHDNode *u2froot) {
static CONFIDENTIAL HDNode node; static CONFIDENTIAL HDNode node;
static CONFIDENTIAL uint8_t seed[64];
char oldTiny = usbTiny(1); char oldTiny = usbTiny(1);
mnemonic_to_seed(mnemonic, "", sessionSeed, mnemonic_to_seed(mnemonic, "", seed, get_u2froot_callback); // BIP-0039
get_u2froot_callback); // BIP-0039
usbTiny(oldTiny); 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); hdnode_private_ckd(&node, U2F_KEY_PATH);
u2froot->depth = node.depth; u2froot->depth = node.depth;
u2froot->child_num = U2F_KEY_PATH; 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, memcpy(u2froot->private_key.bytes, node.private_key,
sizeof(node.private_key)); sizeof(node.private_key));
memzero(&node, sizeof(node)); memzero(&node, sizeof(node));
memzero(&seed, sizeof(seed));
session_clear(false); // invalidate seed cache 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) { const uint8_t *config_getSeed(void) {
// root node is properly cached // root node is properly cached
if (sectrue == sessionSeedCached) { if ((activeSessionCache != NULL) &&
return sessionSeed; (activeSessionCache->seedCached == sectrue)) {
return activeSessionCache->seed;
} }
// if storage has mnemonic, convert it to node and use it // 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); 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 get_root_node_callback); // BIP-0039
memzero(mnemonic, sizeof(mnemonic)); memzero(mnemonic, sizeof(mnemonic));
memzero(passphrase, sizeof(passphrase)); memzero(passphrase, sizeof(passphrase));
usbTiny(oldTiny); usbTiny(oldTiny);
sessionSeedCached = sectrue; activeSessionCache->seedCached = sectrue;
return sessionSeed; return activeSessionCache->seed;
} else { } else {
fsm_sendFailure(FailureType_Failure_NotInitialized, fsm_sendFailure(FailureType_Failure_NotInitialized,
_("Device not initialized")); _("Device not initialized"));
@ -772,12 +803,51 @@ bool config_changeWipeCode(const char *pin, const char *wipe_code) {
return sectrue == ret; return sectrue == ret;
} }
const uint8_t *session_getSessionId(void) { uint8_t session_findLeastRecent(void) {
if (!sessionIdCached) { uint8_t least_recent_index = MAX_SESSIONS_COUNT;
random_buffer(sessionId, 32); 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; ensure(sectrue * (least_recent_index < MAX_SESSIONS_COUNT), NULL);
return sessionId; 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(); } bool session_isUnlocked(void) { return sectrue == storage_is_unlocked(); }

@ -88,6 +88,7 @@ extern Storage configUpdate;
void config_init(void); void config_init(void);
void session_clear(bool lock); void session_clear(bool lock);
void config_lockDevice(void);
void config_loadDevice(const LoadDevice *msg); 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); bool config_getHomescreen(uint8_t *dest, uint16_t dest_size);
void config_setHomescreen(const uint8_t *data, uint32_t 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_setMnemonic(const char *mnemonic);
bool config_containsMnemonic(const char *mnemonic); bool config_containsMnemonic(const char *mnemonic);

@ -17,31 +17,7 @@
* along with this library. If not, see <http://www.gnu.org/licenses/>. * along with this library. If not, see <http://www.gnu.org/licenses/>.
*/ */
void fsm_msgInitialize(const Initialize *msg) { bool get_features(Features *resp) {
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);
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;
@ -103,7 +79,35 @@ void fsm_msgGetFeatures(const GetFeatures *msg) {
resp->capabilities[6] = Capability_Capability_Stellar; resp->capabilities[6] = Capability_Capability_Stellar;
resp->capabilities[7] = Capability_Capability_U2F; resp->capabilities[7] = Capability_Capability_U2F;
#endif #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); msg_write(MessageType_MessageType_Features, resp);
} }
@ -343,7 +347,9 @@ void fsm_msgCancel(const Cancel *msg) {
void fsm_msgClearSession(const ClearSession *msg) { void fsm_msgClearSession(const ClearSession *msg) {
(void)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(); layoutScreensaver();
fsm_sendSuccess(_("Session cleared")); fsm_sendSuccess(_("Session cleared"));
} }

@ -72,7 +72,7 @@ void check_lock_screen(void) {
if (button.YesUp) { if (button.YesUp) {
// lock the screen // lock the screen
session_clear(true); config_lockDevice();
layoutScreensaver(); layoutScreensaver();
} else { } else {
// resume homescreen // resume homescreen
@ -85,7 +85,7 @@ void check_lock_screen(void) {
if ((timer_ms() - system_millis_lock_start) >= if ((timer_ms() - system_millis_lock_start) >=
config_getAutoLockDelayMs()) { config_getAutoLockDelayMs()) {
// lock the screen // lock the screen
session_clear(true); config_lockDevice();
layoutScreensaver(); layoutScreensaver();
} }
} }

Loading…
Cancel
Save