Added storage area for u2f counter.

To prevent flashing for every u2f operation just clear one bit in
the u2f area to indicate an increased counter.
This commit is contained in:
Jochen Hoenicke 2016-05-24 23:44:40 +02:00
parent 8e7896456a
commit e093371129
1 changed files with 76 additions and 22 deletions

View File

@ -48,13 +48,15 @@ char storage_uuid_str[25];
/*
storage layout:
offset | type/length | description
--------+-------------+-------------------------------
0x0000 | 4 bytes | magic = 'stor'
0x0004 | 12 bytes | uuid
0x0010 | ? | Storage structure
0x4000 | 4 kbytes | area for pin failures
0x5000 | 12 kbytes | reserved
offset | type/length | description
--------+--------------+-------------------------------
0x0000 | 4 bytes | magic = 'stor'
0x0004 | 12 bytes | uuid
0x0010 | ? bytes | Storage structure
--------+--------------+-------------------------------
0x4000 | 4 kbytes | area for pin failures
0x5000 | 256 bytes | area for u2f counter updates
0x5100 | 11.75 kbytes | reserved
The area for pin failures looks like this:
0 ... 0 pinfail 0xffffffff .. 0xffffffff
@ -63,15 +65,28 @@ the number of zeros is the number of pin failures.
This layout is used because we can only clear bits without
erasing the flash.
The area for u2f counter updates is just a sequence of zero-bits
followed by a sequence of one-bits. The bits in a byte are numbered
from LSB to MSB. The number of zero bits is the offset that should
be added to the storage u2f_counter to get the real counter value.
*/
#define FLASH_STORAGE_PINAREA (FLASH_META_START + 0x4000)
#define FLASH_STORAGE_PINAREA_LEN (0x1000)
#define FLASH_STORAGE_U2FAREA (FLASH_STORAGE_PINAREA + FLASH_STORAGE_PINAREA_LEN)
#define FLASH_STORAGE_U2FAREA_LEN (0x100)
#define FLASH_STORAGE_REALLEN (4 + sizeof(storage_uuid) + sizeof(Storage))
_Static_assert(FLASH_STORAGE_START + FLASH_STORAGE_REALLEN <= FLASH_STORAGE_PINAREA, "Storage struct is too large for TREZOR flash");
_Static_assert((sizeof(storage_uuid) & 3) == 0, "storage uuid unaligned");
_Static_assert((sizeof(storage) & 3) == 0, "storage unaligned");
/* Current u2f offset, i.e. u2f counter is
* storage.u2f_counter + storage_u2f_offset.
* This corresponds to the number of cleared bits in the U2FAREA.
*/
static uint32_t storage_u2f_offset;
static bool sessionSeedCached, sessionSeedUsesPassphrase;
static uint8_t sessionSeed[64];
@ -132,6 +147,15 @@ bool storage_from_flash(void)
storage.has_pin_failed_attempts = false;
storage.pin_failed_attempts = 0;
}
uint32_t *u2fptr = (uint32_t*) FLASH_STORAGE_U2FAREA;
while (*u2fptr == 0)
u2fptr++;
storage_u2f_offset = 32 * (u2fptr - (uint32_t*) FLASH_STORAGE_U2FAREA);
uint32_t u2fword = *u2fptr;
while ((u2fword & 1) == 0) {
storage_u2f_offset++;
u2fword >>= 1;
}
// upgrade storage version
if (version != STORAGE_VERSION) {
storage.version = STORAGE_VERSION;
@ -185,15 +209,13 @@ static uint32_t storage_flash_words(uint32_t addr, uint32_t *src, int nwords) {
return addr;
}
void storage_commit(void)
static void storage_commit_locked(void)
{
uint8_t meta_backup[FLASH_META_DESC_LEN];
// backup meta
memcpy(meta_backup, (uint8_t*)FLASH_META_START, FLASH_META_DESC_LEN);
flash_clear_status_flags();
flash_unlock();
// erase storage
flash_erase_sector(FLASH_META_SECTOR_FIRST, FLASH_CR_PROGRAM_X32);
// copy meta
@ -209,6 +231,13 @@ void storage_commit(void)
flash_program_word(flash, 0);
flash += 4;
}
}
void storage_commit(void)
{
flash_clear_status_flags();
flash_unlock();
storage_commit_locked();
flash_lock();
storage_check_flash_errors();
}
@ -429,23 +458,38 @@ bool session_isPinCached(void)
return sessionPinCached;
}
void storage_clearPinArea()
void storage_clearPinArea(void)
{
flash_clear_status_flags();
flash_unlock();
flash_erase_sector(FLASH_META_SECTOR_LAST, FLASH_CR_PROGRAM_X32);
flash_lock();
storage_check_flash_errors();
storage_u2f_offset = 0;
}
// called when u2f area or pin area overflows
static void storage_area_recycle(uint32_t new_pinfails)
{
// erase storage sector
flash_erase_sector(FLASH_META_SECTOR_LAST, FLASH_CR_PROGRAM_X32);
flash_program_word(FLASH_STORAGE_PINAREA, new_pinfails);
if (storage_u2f_offset > 0) {
storage.has_u2f_counter = true;
storage.u2f_counter += storage_u2f_offset;
storage_commit_locked();
storage_u2f_offset = 0;
}
}
void storage_resetPinFails(uint32_t *pinfailsptr)
{
flash_clear_status_flags();
flash_unlock();
if ((uint32_t) (pinfailsptr + 1) - FLASH_STORAGE_PINAREA
>= FLASH_STORAGE_PINAREA_LEN) {
// erase extra storage sector
flash_erase_sector(FLASH_META_SECTOR_LAST, FLASH_CR_PROGRAM_X32);
if ((uint32_t) (pinfailsptr + 1)
>= FLASH_STORAGE_PINAREA + FLASH_STORAGE_PINAREA_LEN) {
// recycle extra storage sector
storage_area_recycle(0xffffffff);
} else {
flash_program_word((uint32_t) pinfailsptr, 0);
}
@ -483,14 +527,24 @@ bool storage_isInitialized(void)
return storage.has_node || storage.has_mnemonic;
}
uint32_t storage_getU2FCounter(void)
{
return storage.u2f_counter + storage_u2f_offset;
}
uint32_t storage_nextU2FCounter(void)
{
if(!storage.has_u2f_counter) {
storage.has_u2f_counter = true;
storage.u2f_counter = 1;
} else {
storage.u2f_counter++;
uint32_t *ptr = ((uint32_t *) FLASH_STORAGE_U2FAREA) + (storage_u2f_offset / 32);
uint32_t newval = 0xfffffffe << (storage_u2f_offset & 31);
flash_clear_status_flags();
flash_unlock();
flash_program_word((uint32_t) ptr, newval);
storage_u2f_offset++;
if (storage_u2f_offset >= 8 * FLASH_STORAGE_U2FAREA_LEN) {
storage_area_recycle(*storage_getPinFailsPtr());
}
storage_commit();
return storage.u2f_counter;
flash_lock();
storage_check_flash_errors();
return storage.u2f_counter + storage_u2f_offset;
}