feat(core): support quad-word only flash

[no changelog]
tychovrahe/u5/flash_qw
tychovrahe 11 months ago
parent 238e3fd7c1
commit 43d132acfa

@ -28,8 +28,6 @@
// NRCW = 4e524357
#define NORCOW_MAGIC_V0 ((uint32_t)0x5743524e)
#define NORCOW_WORD_SIZE (sizeof(uint32_t))
#define NORCOW_PREFIX_LEN NORCOW_WORD_SIZE
#define NORCOW_MAGIC_LEN NORCOW_WORD_SIZE
#define NORCOW_VERSION_LEN NORCOW_WORD_SIZE
@ -39,10 +37,26 @@
// The key value which is used to indicate that the entry has been deleted.
#define NORCOW_KEY_DELETED (0x0000)
#ifdef FLASH_BYTE_ACCESS
#define NORCOW_WORD_SIZE (sizeof(uint32_t))
#define NORCOW_PREFIX_LEN (NORCOW_WORD_SIZE)
#define NORCOW_KEY_LEN 2
#define NORCOW_LEN_LEN 2
#define ALIGN(X) ((X) = ((X) + 3) & ~3)
// The offset from the beginning of the sector where stored items start.
#define NORCOW_STORAGE_START \
(NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN + NORCOW_VERSION_LEN)
#else
#define NORCOW_WORD_SIZE (4 * sizeof(uint32_t))
#define NORCOW_PREFIX_LEN (2 * NORCOW_WORD_SIZE)
#define NORCOW_KEY_LEN 16
#define NORCOW_LEN_LEN 16
#define ALIGN(X) ((X) = ((X) + 0xF) & ~0xF)
// The offset from the beginning of the sector where stored items start.
#define NORCOW_STORAGE_START (NORCOW_HEADER_LEN + NORCOW_WORD_SIZE)
#endif
// The index of the active reading sector and writing sector. These should be
// equal except when storage version upgrade or compaction is in progress.
static uint8_t norcow_active_sector = 0;
@ -66,7 +80,8 @@ static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) {
/*
* Writes data to given sector, starting from offset
*/
static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix,
#ifdef FLASH_BYTE_ACCESS
static secbool norcow_write(uint8_t sector, uint32_t offset, uint16_t key,
const uint8_t *data, uint16_t len) {
if (sector >= NORCOW_SECTOR_COUNT) {
return secfalse;
@ -76,6 +91,8 @@ static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix,
return secfalse;
}
uint32_t prefix = ((uint32_t)len << 16) | key;
ensure(flash_unlock_write(), NULL);
// write prefix
@ -100,6 +117,62 @@ static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix,
ensure(flash_lock_write(), NULL);
return sectrue;
}
#else
static secbool norcow_write(uint8_t sector, uint32_t offset, uint16_t key,
const uint8_t *data, uint16_t len) {
if (sector >= NORCOW_SECTOR_COUNT) {
return secfalse;
}
uint16_t len_adjusted = ((len) + 0xF) & ~0xF;
if (offset + NORCOW_PREFIX_LEN + len_adjusted > NORCOW_SECTOR_SIZE) {
return secfalse;
}
ensure(flash_unlock_write(), NULL);
uint32_t d[4];
d[0] = key;
d[1] = 0xFFFFFFFF;
d[2] = 0xFFFFFFFF;
d[3] = 0xFFFFFFFF;
// write key
ensure(flash_area_write_quadword(&STORAGE_AREAS[sector], offset, d), NULL);
offset += NORCOW_WORD_SIZE;
d[0] = len;
// write len
ensure(flash_area_write_quadword(&STORAGE_AREAS[sector], offset, d), NULL);
offset += NORCOW_WORD_SIZE;
if (data != NULL) {
// write data
for (uint16_t i = 0; i < len / NORCOW_WORD_SIZE;
i++, offset += NORCOW_WORD_SIZE) {
memcpy(d, data + i * NORCOW_WORD_SIZE, NORCOW_WORD_SIZE);
ensure(flash_area_write_quadword(&STORAGE_AREAS[sector], offset, d),
NULL);
}
memset(d, 0, sizeof(d));
// pad with zeroes
for (uint16_t i = 0; i < len % NORCOW_WORD_SIZE; i++) {
((uint8_t *)d)[i] = data[i + len - len % NORCOW_WORD_SIZE];
}
if (len % NORCOW_WORD_SIZE != 0) {
ensure(flash_area_write_quadword(&STORAGE_AREAS[sector], offset, d),
NULL);
}
} else {
offset += len;
}
ensure(flash_lock_write(), NULL);
return sectrue;
}
#endif
/*
* Erases sector (and sets a magic)
@ -126,16 +199,36 @@ static void erase_sector(uint8_t sector, secbool set_magic) {
#endif
if (sectrue == set_magic) {
ensure(norcow_write(sector, NORCOW_HEADER_LEN, NORCOW_MAGIC, NULL, 0),
"set magic failed");
ensure(norcow_write(sector, NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN,
~NORCOW_VERSION, NULL, 0),
#ifdef FLASH_BYTE_ACCESS
ensure(flash_unlock_write(), NULL);
ensure(flash_area_write_word(&STORAGE_AREAS[sector], NORCOW_HEADER_LEN,
NORCOW_MAGIC),
NULL);
ensure(flash_area_write_word(&STORAGE_AREAS[sector],
NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN,
~NORCOW_VERSION),
"set version failed");
ensure(flash_lock_write(), NULL);
#else
uint32_t d[4];
d[0] = NORCOW_MAGIC;
d[1] = ~NORCOW_VERSION;
d[2] = 0xFFFFFFFF;
d[3] = 0xFFFFFFFF;
ensure(flash_unlock_write(), NULL);
ensure(
flash_area_write_quadword(&STORAGE_AREAS[sector], NORCOW_HEADER_LEN, d),
"set magic and version failed");
ensure(flash_lock_write(), NULL);
#endif
}
}
#define ALIGN4(X) (X) = ((X) + 3) & ~3
/*
* Reads one item starting from offset
*/
@ -145,7 +238,7 @@ static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key,
const void *k = norcow_ptr(sector, *pos, 2);
if (k == NULL) return secfalse;
*pos += 2;
*pos += NORCOW_KEY_LEN;
memcpy(key, k, sizeof(uint16_t));
if (*key == NORCOW_KEY_FREE) {
return secfalse;
@ -153,13 +246,13 @@ static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key,
const void *l = norcow_ptr(sector, *pos, 2);
if (l == NULL) return secfalse;
*pos += 2;
*pos += NORCOW_LEN_LEN;
memcpy(len, l, sizeof(uint16_t));
*val = norcow_ptr(sector, *pos, *len);
if (*val == NULL) return secfalse;
*pos += *len;
ALIGN4(*pos);
ALIGN(*pos);
return sectrue;
}
@ -168,10 +261,9 @@ static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key,
*/
static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key,
const void *val, uint16_t len, uint32_t *pos) {
uint32_t prefix = ((uint32_t)len << 16) | key;
*pos = offset + NORCOW_PREFIX_LEN + len;
ALIGN4(*pos);
return norcow_write(sector, offset, prefix, val, len);
ALIGN(*pos);
return norcow_write(sector, offset, key, val, len);
}
/*
@ -417,6 +509,7 @@ secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len,
return secfalse;
}
const flash_area_t *area = &STORAGE_AREAS[norcow_write_sector];
secbool ret = secfalse;
const void *ptr = NULL;
uint16_t len_old = 0;
@ -428,19 +521,20 @@ secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len,
offset =
(const uint8_t *)ptr -
(const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE);
#ifdef FLASH_BYTE_ACCESS
if (val != NULL && len_old == len) {
ret = sectrue;
ensure(flash_unlock_write(), NULL);
for (uint16_t i = 0; i < len; i++) {
if (sectrue !=
flash_area_write_byte(&STORAGE_AREAS[norcow_write_sector],
offset + i, ((const uint8_t *)val)[i])) {
if (sectrue != flash_area_write_byte(area, offset + i,
((const uint8_t *)val)[i])) {
ret = secfalse;
break;
}
}
ensure(flash_lock_write(), NULL);
}
#endif
}
// If the update was not possible then write the entry as a new item.
@ -449,18 +543,26 @@ secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len,
if (sectrue == *found) {
ensure(flash_unlock_write(), NULL);
#ifdef FLASH_BYTE_ACCESS
// Update the prefix to indicate that the old item has been deleted.
uint32_t prefix = (uint32_t)len_old << 16;
ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector],
offset - NORCOW_PREFIX_LEN, prefix),
ensure(flash_area_write_word(area, offset - NORCOW_PREFIX_LEN, prefix),
NULL);
#else
// Update the key to indicate that the old item has been deleted.
uint32_t d[4] = {0};
ensure(flash_area_write_quadword(area, offset - NORCOW_PREFIX_LEN, d),
NULL);
#endif
// Delete the old item data.
uint32_t end = offset + len_old;
while (offset < end) {
ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector],
offset, 0x00000000),
NULL);
#ifdef FLASH_BYTE_ACCESS
ensure(flash_area_write_word(area, offset, 0x00000000), NULL);
#else
ensure(flash_area_write_quadword(area, offset, d), NULL);
#endif
offset += NORCOW_WORD_SIZE;
}
@ -490,6 +592,7 @@ secbool norcow_delete(uint16_t key) {
return secfalse;
}
const flash_area_t *area = &STORAGE_AREAS[norcow_write_sector];
const void *ptr = NULL;
uint16_t len = 0;
if (sectrue != find_item(norcow_write_sector, key, &ptr, &len)) {
@ -502,18 +605,24 @@ secbool norcow_delete(uint16_t key) {
ensure(flash_unlock_write(), NULL);
#ifdef FLASH_BYTE_ACCESS
// Update the prefix to indicate that the item has been deleted.
uint32_t prefix = (uint32_t)len << 16;
ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector],
offset - NORCOW_PREFIX_LEN, prefix),
NULL);
ensure(flash_area_write_word(area, offset - NORCOW_PREFIX_LEN, prefix), NULL);
#else
// Update the key to indicate that the old item has been deleted.
uint32_t d[4] = {0};
ensure(flash_area_write_quadword(area, offset - NORCOW_PREFIX_LEN, d), NULL);
#endif
// Delete the item data.
uint32_t end = offset + len;
while (offset < end) {
ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector], offset,
0x00000000),
NULL);
#ifdef FLASH_BYTE_ACCESS
ensure(flash_area_write_word(area, offset, 0x00000000), NULL);
#else
ensure(flash_area_write_quadword(area, offset, d), NULL);
#endif
offset += NORCOW_WORD_SIZE;
}
@ -522,6 +631,7 @@ secbool norcow_delete(uint16_t key) {
return sectrue;
}
#ifdef FLASH_BYTE_ACCESS
/*
* Update a word in flash at the given pointer. The pointer must point
* into the NORCOW area.
@ -546,6 +656,7 @@ secbool norcow_update_word(uint16_t key, uint16_t offset, uint32_t value) {
ensure(flash_lock_write(), NULL);
return sectrue;
}
#endif
/*
* Update the value of the given key starting at the given offset.
@ -564,12 +675,28 @@ secbool norcow_update_bytes(const uint16_t key, const uint16_t offset,
(const uint8_t *)ptr -
(const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE) +
offset;
const flash_area_t *area = &STORAGE_AREAS[norcow_write_sector];
ensure(flash_unlock_write(), NULL);
#ifdef FLASH_BYTE_ACCESS
for (uint16_t i = 0; i < len; i++, sector_offset++) {
ensure(flash_area_write_byte(&STORAGE_AREAS[norcow_write_sector],
sector_offset, data[i]),
NULL);
ensure(flash_area_write_byte(area, sector_offset, data[i]), NULL);
}
#else
uint32_t d[4];
for (uint16_t i = 0; i < len / 16; i++) {
memcpy(d, data + i * 16, 16);
ensure(flash_area_write_quadword(area, sector_offset, d), NULL);
sector_offset += 16;
}
memset(d, 0, 16);
// pad with zeroes
for (uint16_t i = 0; i < len % 16; i++) {
((uint8_t *)d)[i] = data[i + len - len % 16];
}
if (len % 16 != 0) {
ensure(flash_area_write_quadword(area, sector_offset, d), NULL);
}
#endif
ensure(flash_lock_write(), NULL);
return sectrue;
}

@ -65,11 +65,13 @@ secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len,
*/
secbool norcow_delete(uint16_t key);
#ifdef FLASH_BYTE_ACCESS
/*
* Update a word in flash in the given key at the given offset.
* Note that you can only change bits from 1 to 0.
*/
secbool norcow_update_word(uint16_t key, uint16_t offset, uint32_t value);
#endif
/*
* Update the value of the given key starting at the given offset.

@ -126,6 +126,12 @@ const uint32_t V0_PIN_EMPTY = 1;
// The length of the ChaCha20 IV (aka nonce) in bytes as per RFC 7539.
#define CHACHA20_IV_SIZE 12
#ifdef FLASH_BYTE_ACCESS
#define CHACHA20_IV_PADDING 0
#else
#define CHACHA20_IV_PADDING 4
#endif
// The length of the ChaCha20 block in bytes.
#define CHACHA20_BLOCK_SIZE 64
@ -145,7 +151,11 @@ const uint8_t WIPE_CODE_EMPTY[] = {0, 0, 0, 0};
#define V2_WIPE_CODE_EMPTY 0
// The length of the counter tail in words.
#ifdef FLASH_BYTE_ACCESS
#define COUNTER_TAIL_WORDS 2
#else
#define COUNTER_TAIL_WORDS 0
#endif
// Values used in the guard key integrity check.
#define GUARD_KEY_MODULUS 6311
@ -404,19 +414,22 @@ static secbool set_wipe_code(const uint8_t *wipe_code, size_t wipe_code_len) {
return secfalse;
}
// Write wipe code into the preallocated entry.
if (sectrue !=
norcow_update_bytes(WIPE_CODE_DATA_KEY, 0, wipe_code, wipe_code_len)) {
return secfalse;
}
uint32_t data[(MAX_WIPE_CODE_LEN + WIPE_CODE_SALT_SIZE + WIPE_CODE_TAG_SIZE) /
sizeof(uint32_t)] = {0};
// Write salt and tag into the preallocated entry.
if (sectrue !=
norcow_update_bytes(WIPE_CODE_DATA_KEY, wipe_code_len, salt_and_tag,
WIPE_CODE_SALT_SIZE + WIPE_CODE_TAG_SIZE)) {
memcpy((uint8_t *)data, wipe_code, wipe_code_len);
memcpy((uint8_t *)data + wipe_code_len, salt_and_tag,
WIPE_CODE_SALT_SIZE + WIPE_CODE_TAG_SIZE);
// Write wipe code into the preallocated entry.
if (sectrue != norcow_update_bytes(WIPE_CODE_DATA_KEY, 0, (uint8_t *)data,
wipe_code_len + WIPE_CODE_SALT_SIZE +
WIPE_CODE_TAG_SIZE)) {
memzero(data, sizeof(data));
return secfalse;
}
memzero(data, sizeof(data));
return sectrue;
}
@ -710,6 +723,12 @@ static secbool pin_fails_reset(void) {
return secfalse;
}
#ifndef FLASH_BYTE_ACCESS
uint32_t new_logs[GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS];
secbool edited = secfalse;
memcpy(new_logs, logs, len);
#endif
uint32_t guard_mask = 0;
uint32_t guard = 0;
wait_random();
@ -723,16 +742,35 @@ static secbool pin_fails_reset(void) {
const uint32_t *entry_log = success_log + PIN_LOG_WORDS;
for (size_t i = 0; i < PIN_LOG_WORDS; ++i) {
if (entry_log[i] == unused) {
#ifndef FLASH_BYTE_ACCESS
if (edited == sectrue) {
return norcow_set(PIN_LOGS_KEY, new_logs, sizeof(new_logs));
}
#endif
return sectrue;
}
if (success_log[i] != guard) {
#ifdef FLASH_BYTE_ACCESS
if (sectrue != norcow_update_word(
PIN_LOGS_KEY, sizeof(uint32_t) * (i + GUARD_KEY_WORDS),
entry_log[i])) {
return secfalse;
}
#else
if (new_logs[(i + GUARD_KEY_WORDS)] != entry_log[i]) {
edited = sectrue;
new_logs[(i + GUARD_KEY_WORDS)] = entry_log[i];
}
#endif
}
}
#ifndef FLASH_BYTE_ACCESS
if (edited == sectrue) {
if (sectrue != norcow_set(PIN_LOGS_KEY, new_logs, sizeof(new_logs))) {
return secfalse;
}
}
#endif
return pin_logs_init(0);
}
@ -751,6 +789,11 @@ secbool storage_pin_fails_increase(void) {
return secfalse;
}
#ifndef FLASH_BYTE_ACCESS
uint32_t new_logs[GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS];
memcpy(new_logs, logs, len);
#endif
uint32_t guard_mask = 0;
uint32_t guard = 0;
wait_random();
@ -775,6 +818,7 @@ secbool storage_pin_fails_increase(void) {
word = (word >> 2) | (word >> 1);
wait_random();
#ifdef FLASH_BYTE_ACCESS
if (sectrue !=
norcow_update_word(
PIN_LOGS_KEY,
@ -783,6 +827,14 @@ secbool storage_pin_fails_increase(void) {
handle_fault("PIN logs update");
return secfalse;
}
#else
new_logs[(i + GUARD_KEY_WORDS + PIN_LOG_WORDS)] =
(word & ~guard_mask) | guard;
if (sectrue != norcow_set(PIN_LOGS_KEY, new_logs, sizeof(new_logs))) {
handle_fault("PIN logs update");
return secfalse;
}
#endif
return sectrue;
}
}
@ -1145,11 +1197,11 @@ static secbool storage_get_encrypted(const uint16_t key, void *val_dest,
return secfalse;
}
if (*len < CHACHA20_IV_SIZE + POLY1305_TAG_SIZE) {
if (*len < CHACHA20_IV_SIZE + CHACHA20_IV_PADDING + POLY1305_TAG_SIZE) {
handle_fault("ciphertext length check");
return secfalse;
}
*len -= CHACHA20_IV_SIZE + POLY1305_TAG_SIZE;
*len -= CHACHA20_IV_SIZE + CHACHA20_IV_PADDING + POLY1305_TAG_SIZE;
if (val_dest == NULL) {
return sectrue;
@ -1160,9 +1212,10 @@ static secbool storage_get_encrypted(const uint16_t key, void *val_dest,
}
const uint8_t *iv = (const uint8_t *)val_stored;
const uint8_t *tag_stored = (const uint8_t *)val_stored + CHACHA20_IV_SIZE;
const uint8_t *ciphertext =
(const uint8_t *)val_stored + CHACHA20_IV_SIZE + POLY1305_TAG_SIZE;
const uint8_t *tag_stored =
(const uint8_t *)val_stored + CHACHA20_IV_SIZE + CHACHA20_IV_PADDING;
const uint8_t *ciphertext = (const uint8_t *)val_stored + CHACHA20_IV_SIZE +
CHACHA20_IV_PADDING + POLY1305_TAG_SIZE;
uint8_t tag_computed[POLY1305_TAG_SIZE] = {0};
chacha20poly1305_ctx ctx = {0};
rfc7539_init(&ctx, cached_dek, iv);
@ -1229,24 +1282,27 @@ secbool storage_get(const uint16_t key, void *val_dest, const uint16_t max_len,
*/
static secbool storage_set_encrypted(const uint16_t key, const void *val,
const uint16_t len) {
if (len > UINT16_MAX - CHACHA20_IV_SIZE - POLY1305_TAG_SIZE) {
if (len >
UINT16_MAX - CHACHA20_IV_SIZE - CHACHA20_IV_PADDING - POLY1305_TAG_SIZE) {
return secfalse;
}
// Preallocate space on the flash storage.
if (sectrue !=
auth_set(key, NULL, CHACHA20_IV_SIZE + POLY1305_TAG_SIZE + len)) {
if (sectrue != auth_set(key, NULL,
CHACHA20_IV_SIZE + CHACHA20_IV_PADDING +
POLY1305_TAG_SIZE + len)) {
return secfalse;
}
// Write the IV to the flash.
uint8_t buffer[CHACHA20_BLOCK_SIZE] = {0};
uint8_t buffer[CHACHA20_BLOCK_SIZE + CHACHA20_IV_PADDING] = {0};
random_buffer(buffer, CHACHA20_IV_SIZE);
uint16_t offset = 0;
if (sectrue != norcow_update_bytes(key, offset, buffer, CHACHA20_IV_SIZE)) {
if (sectrue != norcow_update_bytes(key, offset, buffer,
CHACHA20_IV_SIZE + CHACHA20_IV_PADDING)) {
return secfalse;
}
offset += CHACHA20_IV_SIZE + POLY1305_TAG_SIZE;
offset += CHACHA20_IV_SIZE + CHACHA20_IV_PADDING + POLY1305_TAG_SIZE;
// Encrypt all blocks except for the last one.
chacha20poly1305_ctx ctx = {0};
@ -1270,7 +1326,8 @@ static secbool storage_set_encrypted(const uint16_t key, const void *val,
secbool ret = norcow_update_bytes(key, offset, buffer, len - i);
if (sectrue == ret) {
rfc7539_finish(&ctx, sizeof(key), len, buffer);
ret = norcow_update_bytes(key, CHACHA20_IV_SIZE, buffer, POLY1305_TAG_SIZE);
ret = norcow_update_bytes(key, CHACHA20_IV_SIZE + CHACHA20_IV_PADDING,
buffer, POLY1305_TAG_SIZE);
}
memzero(&ctx, sizeof(ctx));
memzero(buffer, sizeof(buffer));
@ -1372,7 +1429,11 @@ secbool storage_next_counter(const uint16_t key, uint32_t *count) {
// Value overflow.
return secfalse;
}
#ifdef FLASH_BYTE_ACCESS
return norcow_update_word(key, sizeof(uint32_t) * i, val_stored[i] >> 1);
#else
return storage_set_counter(key, *count);
#endif
} else {
return storage_set_counter(key, *count);
}

Loading…
Cancel
Save