mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-07 15:18:08 +00:00
feat(core): add support for quad-word only storage
[no changelog]
This commit is contained in:
parent
04df2f0de3
commit
f1cbcdfde6
@ -117,6 +117,9 @@ secbool flash_area_write_word(const flash_area_t *area, uint32_t offset,
|
|||||||
|
|
||||||
secbool flash_area_write_quadword(const flash_area_t *area, uint32_t offset,
|
secbool flash_area_write_quadword(const flash_area_t *area, uint32_t offset,
|
||||||
const uint32_t *data) {
|
const uint32_t *data) {
|
||||||
|
if (offset % 16 != 0) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (sectrue !=
|
if (sectrue !=
|
||||||
flash_area_write_word(area, offset + i * sizeof(uint32_t), data[i])) {
|
flash_area_write_word(area, offset + i * sizeof(uint32_t), data[i])) {
|
||||||
|
340
storage/norcow.c
340
storage/norcow.c
@ -28,8 +28,6 @@
|
|||||||
// NRCW = 4e524357
|
// NRCW = 4e524357
|
||||||
#define NORCOW_MAGIC_V0 ((uint32_t)0x5743524e)
|
#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_MAGIC_LEN NORCOW_WORD_SIZE
|
||||||
#define NORCOW_VERSION_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.
|
// The key value which is used to indicate that the entry has been deleted.
|
||||||
#define NORCOW_KEY_DELETED (0x0000)
|
#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.
|
// The offset from the beginning of the sector where stored items start.
|
||||||
#define NORCOW_STORAGE_START \
|
#define NORCOW_STORAGE_START \
|
||||||
(NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN + NORCOW_VERSION_LEN)
|
(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
|
// The index of the active reading sector and writing sector. These should be
|
||||||
// equal except when storage version upgrade or compaction is in progress.
|
// equal except when storage version upgrade or compaction is in progress.
|
||||||
static uint8_t norcow_active_sector = 0;
|
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
|
* 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) {
|
const uint8_t *data, uint16_t len) {
|
||||||
if (sector >= NORCOW_SECTOR_COUNT) {
|
if (sector >= NORCOW_SECTOR_COUNT) {
|
||||||
return secfalse;
|
return secfalse;
|
||||||
@ -76,6 +91,8 @@ static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix,
|
|||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t prefix = ((uint32_t)len << 16) | key;
|
||||||
|
|
||||||
ensure(flash_unlock_write(), NULL);
|
ensure(flash_unlock_write(), NULL);
|
||||||
|
|
||||||
// write prefix
|
// write prefix
|
||||||
@ -100,6 +117,85 @@ static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix,
|
|||||||
ensure(flash_lock_write(), NULL);
|
ensure(flash_lock_write(), NULL);
|
||||||
return sectrue;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len <= 12) {
|
||||||
|
// the whole item fits into one quadword, let's not waste space
|
||||||
|
uint16_t len_adjusted = 0x10;
|
||||||
|
|
||||||
|
if (offset + len_adjusted > NORCOW_SECTOR_SIZE) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure(flash_unlock_write(), NULL);
|
||||||
|
|
||||||
|
uint32_t d[4];
|
||||||
|
d[0] = len | ((uint32_t)key << 16);
|
||||||
|
d[1] = 0;
|
||||||
|
d[2] = 0;
|
||||||
|
d[3] = 0;
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
memcpy(&d[1], data, len); // write len
|
||||||
|
}
|
||||||
|
ensure(flash_area_write_quadword(&STORAGE_AREAS[sector], offset, d), NULL);
|
||||||
|
ensure(flash_lock_write(), NULL);
|
||||||
|
} else {
|
||||||
|
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] = len;
|
||||||
|
d[1] = 0xFFFFFFFF;
|
||||||
|
d[2] = 0xFFFFFFFF;
|
||||||
|
d[3] = 0xFFFFFFFF;
|
||||||
|
// write len
|
||||||
|
ensure(flash_area_write_quadword(&STORAGE_AREAS[sector], offset, d), NULL);
|
||||||
|
offset += NORCOW_WORD_SIZE;
|
||||||
|
|
||||||
|
d[0] = key;
|
||||||
|
// write key
|
||||||
|
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)
|
* Erases sector (and sets a magic)
|
||||||
@ -126,16 +222,36 @@ static void erase_sector(uint8_t sector, secbool set_magic) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (sectrue == set_magic) {
|
if (sectrue == set_magic) {
|
||||||
ensure(norcow_write(sector, NORCOW_HEADER_LEN, NORCOW_MAGIC, NULL, 0),
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
"set magic failed");
|
ensure(flash_unlock_write(), NULL);
|
||||||
ensure(norcow_write(sector, NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN,
|
|
||||||
~NORCOW_VERSION, NULL, 0),
|
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");
|
"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
|
* Reads one item starting from offset
|
||||||
*/
|
*/
|
||||||
@ -143,9 +259,10 @@ static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key,
|
|||||||
const void **val, uint16_t *len, uint32_t *pos) {
|
const void **val, uint16_t *len, uint32_t *pos) {
|
||||||
*pos = offset;
|
*pos = offset;
|
||||||
|
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
const void *k = norcow_ptr(sector, *pos, 2);
|
const void *k = norcow_ptr(sector, *pos, 2);
|
||||||
if (k == NULL) return secfalse;
|
if (k == NULL) return secfalse;
|
||||||
*pos += 2;
|
*pos += NORCOW_KEY_LEN;
|
||||||
memcpy(key, k, sizeof(uint16_t));
|
memcpy(key, k, sizeof(uint16_t));
|
||||||
if (*key == NORCOW_KEY_FREE) {
|
if (*key == NORCOW_KEY_FREE) {
|
||||||
return secfalse;
|
return secfalse;
|
||||||
@ -153,13 +270,39 @@ static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key,
|
|||||||
|
|
||||||
const void *l = norcow_ptr(sector, *pos, 2);
|
const void *l = norcow_ptr(sector, *pos, 2);
|
||||||
if (l == NULL) return secfalse;
|
if (l == NULL) return secfalse;
|
||||||
*pos += 2;
|
*pos += NORCOW_LEN_LEN;
|
||||||
memcpy(len, l, sizeof(uint16_t));
|
memcpy(len, l, sizeof(uint16_t));
|
||||||
|
#else
|
||||||
|
const void *l = norcow_ptr(sector, *pos, 2);
|
||||||
|
if (l == NULL) return secfalse;
|
||||||
|
memcpy(len, l, sizeof(uint16_t));
|
||||||
|
|
||||||
|
if (*len <= 12) {
|
||||||
|
*pos += 2;
|
||||||
|
const void *k = norcow_ptr(sector, *pos, 2);
|
||||||
|
if (k == NULL) return secfalse;
|
||||||
|
memcpy(key, k, sizeof(uint16_t));
|
||||||
|
if (*key == NORCOW_KEY_FREE) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
*pos += 2;
|
||||||
|
} else {
|
||||||
|
*pos += NORCOW_LEN_LEN;
|
||||||
|
const void *k = norcow_ptr(sector, *pos, 2);
|
||||||
|
if (k == NULL) return secfalse;
|
||||||
|
*pos += NORCOW_KEY_LEN;
|
||||||
|
memcpy(key, k, sizeof(uint16_t));
|
||||||
|
if (*key == NORCOW_KEY_FREE) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
*val = norcow_ptr(sector, *pos, *len);
|
*val = norcow_ptr(sector, *pos, *len);
|
||||||
if (*val == NULL) return secfalse;
|
if (*val == NULL) return secfalse;
|
||||||
*pos += *len;
|
*pos += *len;
|
||||||
ALIGN4(*pos);
|
ALIGN(*pos);
|
||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +311,20 @@ 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,
|
static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key,
|
||||||
const void *val, uint16_t len, uint32_t *pos) {
|
const void *val, uint16_t len, uint32_t *pos) {
|
||||||
uint32_t prefix = ((uint32_t)len << 16) | key;
|
#ifndef FLASH_BYTE_ACCESS
|
||||||
|
if (len > 12) {
|
||||||
|
*pos = offset + NORCOW_PREFIX_LEN + len;
|
||||||
|
ALIGN(*pos);
|
||||||
|
} else {
|
||||||
|
*pos = offset + NORCOW_WORD_SIZE;
|
||||||
|
ALIGN(*pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
*pos = offset + NORCOW_PREFIX_LEN + len;
|
*pos = offset + NORCOW_PREFIX_LEN + len;
|
||||||
ALIGN4(*pos);
|
ALIGN(*pos);
|
||||||
return norcow_write(sector, offset, prefix, val, len);
|
#endif
|
||||||
|
return norcow_write(sector, offset, key, val, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -417,6 +570,7 @@ secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len,
|
|||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const flash_area_t *area = &STORAGE_AREAS[norcow_write_sector];
|
||||||
secbool ret = secfalse;
|
secbool ret = secfalse;
|
||||||
const void *ptr = NULL;
|
const void *ptr = NULL;
|
||||||
uint16_t len_old = 0;
|
uint16_t len_old = 0;
|
||||||
@ -428,19 +582,83 @@ secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len,
|
|||||||
offset =
|
offset =
|
||||||
(const uint8_t *)ptr -
|
(const uint8_t *)ptr -
|
||||||
(const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE);
|
(const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE);
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
if (val != NULL && len_old == len) {
|
if (val != NULL && len_old == len) {
|
||||||
ret = sectrue;
|
ret = sectrue;
|
||||||
ensure(flash_unlock_write(), NULL);
|
ensure(flash_unlock_write(), NULL);
|
||||||
for (uint16_t i = 0; i < len; i++) {
|
for (uint16_t i = 0; i < len; i++) {
|
||||||
if (sectrue !=
|
if (sectrue != flash_area_write_byte(area, offset + i,
|
||||||
flash_area_write_byte(&STORAGE_AREAS[norcow_write_sector],
|
((const uint8_t *)val)[i])) {
|
||||||
offset + i, ((const uint8_t *)val)[i])) {
|
|
||||||
ret = secfalse;
|
ret = secfalse;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ensure(flash_lock_write(), NULL);
|
ensure(flash_lock_write(), NULL);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (val != NULL && len_old == len) {
|
||||||
|
if (len <= 12) {
|
||||||
|
secbool updatable = sectrue;
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < len; i++) {
|
||||||
|
if (((const uint8_t *)val)[i] != ((const uint8_t *)ptr)[i]) {
|
||||||
|
updatable = secfalse;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatable == sectrue) {
|
||||||
|
ret = sectrue;
|
||||||
|
|
||||||
|
ensure(flash_unlock_write(), NULL);
|
||||||
|
|
||||||
|
uint32_t d[4];
|
||||||
|
d[0] = len | ((uint32_t)key << 16);
|
||||||
|
d[1] = 0;
|
||||||
|
d[2] = 0;
|
||||||
|
d[3] = 0;
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
memcpy(&d[1], val, len); // write len
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure(flash_area_write_quadword(area, offset - 4, d), NULL);
|
||||||
|
|
||||||
|
ensure(flash_lock_write(), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
secbool updatable = sectrue;
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < len; i++) {
|
||||||
|
if (((const uint8_t *)val)[i] != ((const uint8_t *)ptr)[i]) {
|
||||||
|
updatable = secfalse;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatable == sectrue) {
|
||||||
|
ret = sectrue;
|
||||||
|
uint8_t *v = (uint8_t *)val;
|
||||||
|
int tmp_len = len;
|
||||||
|
|
||||||
|
ensure(flash_unlock_write(), NULL);
|
||||||
|
while (tmp_len > 0) {
|
||||||
|
uint32_t d[4] = {0};
|
||||||
|
memcpy(d, v,
|
||||||
|
((size_t)tmp_len > NORCOW_WORD_SIZE ? NORCOW_WORD_SIZE
|
||||||
|
: (size_t)tmp_len));
|
||||||
|
ensure(flash_area_write_quadword(area, offset, d), NULL);
|
||||||
|
offset += NORCOW_WORD_SIZE;
|
||||||
|
v += NORCOW_WORD_SIZE;
|
||||||
|
tmp_len -= NORCOW_WORD_SIZE;
|
||||||
|
}
|
||||||
|
ensure(flash_lock_write(), NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the update was not possible then write the entry as a new item.
|
// If the update was not possible then write the entry as a new item.
|
||||||
@ -449,18 +667,36 @@ secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len,
|
|||||||
if (sectrue == *found) {
|
if (sectrue == *found) {
|
||||||
ensure(flash_unlock_write(), NULL);
|
ensure(flash_unlock_write(), NULL);
|
||||||
|
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
// Update the prefix to indicate that the old item has been deleted.
|
// Update the prefix to indicate that the old item has been deleted.
|
||||||
uint32_t prefix = (uint32_t)len_old << 16;
|
uint32_t prefix = (uint32_t)len_old << 16;
|
||||||
ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector],
|
ensure(flash_area_write_word(area, offset - NORCOW_PREFIX_LEN, prefix),
|
||||||
offset - NORCOW_PREFIX_LEN, prefix),
|
|
||||||
NULL);
|
NULL);
|
||||||
|
#else
|
||||||
|
if (len_old <= 12) {
|
||||||
|
// Delete entire content of the quadword.
|
||||||
|
uint32_t d[4] = {0};
|
||||||
|
ensure(flash_area_write_quadword(area, offset - 4, d), 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 + NORCOW_LEN_LEN, d),
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Delete the old item data.
|
// Delete the old item data.
|
||||||
uint32_t end = offset + len_old;
|
uint32_t end = offset + len_old;
|
||||||
while (offset < end) {
|
while (offset < end) {
|
||||||
ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector],
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
offset, 0x00000000),
|
ensure(flash_area_write_word(area, offset, 0x00000000), NULL);
|
||||||
NULL);
|
#else
|
||||||
|
if (len_old > 12) {
|
||||||
|
uint32_t d[4] = {0};
|
||||||
|
ensure(flash_area_write_quadword(area, offset, d), NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
offset += NORCOW_WORD_SIZE;
|
offset += NORCOW_WORD_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,6 +726,7 @@ secbool norcow_delete(uint16_t key) {
|
|||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const flash_area_t *area = &STORAGE_AREAS[norcow_write_sector];
|
||||||
const void *ptr = NULL;
|
const void *ptr = NULL;
|
||||||
uint16_t len = 0;
|
uint16_t len = 0;
|
||||||
if (sectrue != find_item(norcow_write_sector, key, &ptr, &len)) {
|
if (sectrue != find_item(norcow_write_sector, key, &ptr, &len)) {
|
||||||
@ -502,18 +739,33 @@ secbool norcow_delete(uint16_t key) {
|
|||||||
|
|
||||||
ensure(flash_unlock_write(), NULL);
|
ensure(flash_unlock_write(), NULL);
|
||||||
|
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
// Update the prefix to indicate that the item has been deleted.
|
// Update the prefix to indicate that the item has been deleted.
|
||||||
uint32_t prefix = (uint32_t)len << 16;
|
uint32_t prefix = (uint32_t)len << 16;
|
||||||
ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector],
|
ensure(flash_area_write_word(area, offset - NORCOW_PREFIX_LEN, prefix), NULL);
|
||||||
offset - NORCOW_PREFIX_LEN, prefix),
|
#else
|
||||||
NULL);
|
uint32_t d[4] = {0};
|
||||||
|
if (len <= 12) {
|
||||||
|
// Delete entire content of the quadword.
|
||||||
|
ensure(flash_area_write_quadword(area, offset - 4, d), NULL);
|
||||||
|
} else {
|
||||||
|
// Update the key to indicate that the old item has been deleted.
|
||||||
|
ensure(flash_area_write_quadword(
|
||||||
|
area, offset - NORCOW_PREFIX_LEN + NORCOW_LEN_LEN, d),
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Delete the item data.
|
// Delete the item data.
|
||||||
uint32_t end = offset + len;
|
uint32_t end = offset + len;
|
||||||
while (offset < end) {
|
while (offset < end) {
|
||||||
ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector], offset,
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
0x00000000),
|
ensure(flash_area_write_word(area, offset, 0x00000000), NULL);
|
||||||
NULL);
|
#else
|
||||||
|
if (len >= 12) {
|
||||||
|
ensure(flash_area_write_quadword(area, offset, d), NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
offset += NORCOW_WORD_SIZE;
|
offset += NORCOW_WORD_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,6 +774,7 @@ secbool norcow_delete(uint16_t key) {
|
|||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
/*
|
/*
|
||||||
* Update a word in flash at the given pointer. The pointer must point
|
* Update a word in flash at the given pointer. The pointer must point
|
||||||
* into the NORCOW area.
|
* into the NORCOW area.
|
||||||
@ -546,6 +799,7 @@ secbool norcow_update_word(uint16_t key, uint16_t offset, uint32_t value) {
|
|||||||
ensure(flash_lock_write(), NULL);
|
ensure(flash_lock_write(), NULL);
|
||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update the value of the given key starting at the given offset.
|
* Update the value of the given key starting at the given offset.
|
||||||
@ -557,6 +811,12 @@ secbool norcow_update_bytes(const uint16_t key, const uint16_t offset,
|
|||||||
if (sectrue != find_item(norcow_write_sector, key, &ptr, &allocated_len)) {
|
if (sectrue != find_item(norcow_write_sector, key, &ptr, &allocated_len)) {
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
#ifndef FLASH_BYTE_ACCESS
|
||||||
|
if (allocated_len <= 12) {
|
||||||
|
// small items are not updated in place
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (offset + len > allocated_len) {
|
if (offset + len > allocated_len) {
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
@ -564,12 +824,28 @@ secbool norcow_update_bytes(const uint16_t key, const uint16_t offset,
|
|||||||
(const uint8_t *)ptr -
|
(const uint8_t *)ptr -
|
||||||
(const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE) +
|
(const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE) +
|
||||||
offset;
|
offset;
|
||||||
|
const flash_area_t *area = &STORAGE_AREAS[norcow_write_sector];
|
||||||
ensure(flash_unlock_write(), NULL);
|
ensure(flash_unlock_write(), NULL);
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
for (uint16_t i = 0; i < len; i++, sector_offset++) {
|
for (uint16_t i = 0; i < len; i++, sector_offset++) {
|
||||||
ensure(flash_area_write_byte(&STORAGE_AREAS[norcow_write_sector],
|
ensure(flash_area_write_byte(area, sector_offset, data[i]), NULL);
|
||||||
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);
|
ensure(flash_lock_write(), NULL);
|
||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#define __NORCOW_H__
|
#define __NORCOW_H__
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "flash.h"
|
||||||
#include "secbool.h"
|
#include "secbool.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -65,11 +66,13 @@ secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len,
|
|||||||
*/
|
*/
|
||||||
secbool norcow_delete(uint16_t key);
|
secbool norcow_delete(uint16_t key);
|
||||||
|
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
/*
|
/*
|
||||||
* Update a word in flash in the given key at the given offset.
|
* Update a word in flash in the given key at the given offset.
|
||||||
* Note that you can only change bits from 1 to 0.
|
* Note that you can only change bits from 1 to 0.
|
||||||
*/
|
*/
|
||||||
secbool norcow_update_word(uint16_t key, uint16_t offset, uint32_t value);
|
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.
|
* Update the value of the given key starting at the given offset.
|
||||||
|
@ -129,6 +129,12 @@ const uint32_t V0_PIN_EMPTY = 1;
|
|||||||
// The length of the ChaCha20 IV (aka nonce) in bytes as per RFC 7539.
|
// The length of the ChaCha20 IV (aka nonce) in bytes as per RFC 7539.
|
||||||
#define CHACHA20_IV_SIZE 12
|
#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.
|
// The length of the ChaCha20 block in bytes.
|
||||||
#define CHACHA20_BLOCK_SIZE 64
|
#define CHACHA20_BLOCK_SIZE 64
|
||||||
|
|
||||||
@ -148,7 +154,11 @@ const uint8_t WIPE_CODE_EMPTY[] = {0, 0, 0, 0};
|
|||||||
#define V2_WIPE_CODE_EMPTY 0
|
#define V2_WIPE_CODE_EMPTY 0
|
||||||
|
|
||||||
// The length of the counter tail in words.
|
// The length of the counter tail in words.
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
#define COUNTER_TAIL_WORDS 2
|
#define COUNTER_TAIL_WORDS 2
|
||||||
|
#else
|
||||||
|
#define COUNTER_TAIL_WORDS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
// Values used in the guard key integrity check.
|
// Values used in the guard key integrity check.
|
||||||
#define GUARD_KEY_MODULUS 6311
|
#define GUARD_KEY_MODULUS 6311
|
||||||
@ -407,19 +417,22 @@ static secbool set_wipe_code(const uint8_t *wipe_code, size_t wipe_code_len) {
|
|||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t data[(MAX_WIPE_CODE_LEN + WIPE_CODE_SALT_SIZE + WIPE_CODE_TAG_SIZE) /
|
||||||
|
sizeof(uint32_t)] = {0};
|
||||||
|
|
||||||
|
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.
|
// Write wipe code into the preallocated entry.
|
||||||
if (sectrue !=
|
if (sectrue != norcow_update_bytes(WIPE_CODE_DATA_KEY, 0, (uint8_t *)data,
|
||||||
norcow_update_bytes(WIPE_CODE_DATA_KEY, 0, wipe_code, wipe_code_len)) {
|
wipe_code_len + WIPE_CODE_SALT_SIZE +
|
||||||
return secfalse;
|
WIPE_CODE_TAG_SIZE)) {
|
||||||
}
|
memzero(data, sizeof(data));
|
||||||
|
|
||||||
// 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)) {
|
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memzero(data, sizeof(data));
|
||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -823,6 +836,12 @@ static secbool pin_fails_reset(void) {
|
|||||||
return secfalse;
|
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_mask = 0;
|
||||||
uint32_t guard = 0;
|
uint32_t guard = 0;
|
||||||
wait_random();
|
wait_random();
|
||||||
@ -836,16 +855,35 @@ static secbool pin_fails_reset(void) {
|
|||||||
const uint32_t *entry_log = success_log + PIN_LOG_WORDS;
|
const uint32_t *entry_log = success_log + PIN_LOG_WORDS;
|
||||||
for (size_t i = 0; i < PIN_LOG_WORDS; ++i) {
|
for (size_t i = 0; i < PIN_LOG_WORDS; ++i) {
|
||||||
if (entry_log[i] == unused) {
|
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;
|
return sectrue;
|
||||||
}
|
}
|
||||||
if (success_log[i] != guard) {
|
if (success_log[i] != guard) {
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
if (sectrue != norcow_update_word(
|
if (sectrue != norcow_update_word(
|
||||||
PIN_LOGS_KEY, sizeof(uint32_t) * (i + GUARD_KEY_WORDS),
|
PIN_LOGS_KEY, sizeof(uint32_t) * (i + GUARD_KEY_WORDS),
|
||||||
entry_log[i])) {
|
entry_log[i])) {
|
||||||
return secfalse;
|
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);
|
return pin_logs_init(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -864,6 +902,11 @@ secbool storage_pin_fails_increase(void) {
|
|||||||
return secfalse;
|
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_mask = 0;
|
||||||
uint32_t guard = 0;
|
uint32_t guard = 0;
|
||||||
wait_random();
|
wait_random();
|
||||||
@ -888,6 +931,7 @@ secbool storage_pin_fails_increase(void) {
|
|||||||
word = (word >> 2) | (word >> 1);
|
word = (word >> 2) | (word >> 1);
|
||||||
|
|
||||||
wait_random();
|
wait_random();
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
if (sectrue !=
|
if (sectrue !=
|
||||||
norcow_update_word(
|
norcow_update_word(
|
||||||
PIN_LOGS_KEY,
|
PIN_LOGS_KEY,
|
||||||
@ -896,6 +940,14 @@ secbool storage_pin_fails_increase(void) {
|
|||||||
handle_fault("PIN logs update");
|
handle_fault("PIN logs update");
|
||||||
return secfalse;
|
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;
|
return sectrue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1250,11 +1302,11 @@ static secbool storage_get_encrypted(const uint16_t key, void *val_dest,
|
|||||||
return secfalse;
|
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");
|
handle_fault("ciphertext length check");
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
*len -= CHACHA20_IV_SIZE + POLY1305_TAG_SIZE;
|
*len -= CHACHA20_IV_SIZE + CHACHA20_IV_PADDING + POLY1305_TAG_SIZE;
|
||||||
|
|
||||||
if (val_dest == NULL) {
|
if (val_dest == NULL) {
|
||||||
return sectrue;
|
return sectrue;
|
||||||
@ -1265,9 +1317,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 *iv = (const uint8_t *)val_stored;
|
||||||
const uint8_t *tag_stored = (const uint8_t *)val_stored + CHACHA20_IV_SIZE;
|
const uint8_t *tag_stored =
|
||||||
const uint8_t *ciphertext =
|
(const uint8_t *)val_stored + CHACHA20_IV_SIZE + CHACHA20_IV_PADDING;
|
||||||
(const uint8_t *)val_stored + CHACHA20_IV_SIZE + POLY1305_TAG_SIZE;
|
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};
|
uint8_t tag_computed[POLY1305_TAG_SIZE] = {0};
|
||||||
chacha20poly1305_ctx ctx = {0};
|
chacha20poly1305_ctx ctx = {0};
|
||||||
rfc7539_init(&ctx, cached_dek, iv);
|
rfc7539_init(&ctx, cached_dek, iv);
|
||||||
@ -1334,24 +1387,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,
|
static secbool storage_set_encrypted(const uint16_t key, const void *val,
|
||||||
const uint16_t len) {
|
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;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preallocate space on the flash storage.
|
// Preallocate space on the flash storage.
|
||||||
if (sectrue !=
|
if (sectrue != auth_set(key, NULL,
|
||||||
auth_set(key, NULL, CHACHA20_IV_SIZE + POLY1305_TAG_SIZE + len)) {
|
CHACHA20_IV_SIZE + CHACHA20_IV_PADDING +
|
||||||
|
POLY1305_TAG_SIZE + len)) {
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the IV to the flash.
|
// 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);
|
random_buffer(buffer, CHACHA20_IV_SIZE);
|
||||||
uint16_t offset = 0;
|
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;
|
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.
|
// Encrypt all blocks except for the last one.
|
||||||
chacha20poly1305_ctx ctx = {0};
|
chacha20poly1305_ctx ctx = {0};
|
||||||
@ -1375,7 +1431,8 @@ static secbool storage_set_encrypted(const uint16_t key, const void *val,
|
|||||||
secbool ret = norcow_update_bytes(key, offset, buffer, len - i);
|
secbool ret = norcow_update_bytes(key, offset, buffer, len - i);
|
||||||
if (sectrue == ret) {
|
if (sectrue == ret) {
|
||||||
rfc7539_finish(&ctx, sizeof(key), len, buffer);
|
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(&ctx, sizeof(ctx));
|
||||||
memzero(buffer, sizeof(buffer));
|
memzero(buffer, sizeof(buffer));
|
||||||
@ -1477,7 +1534,11 @@ secbool storage_next_counter(const uint16_t key, uint32_t *count) {
|
|||||||
// Value overflow.
|
// Value overflow.
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
#ifdef FLASH_BYTE_ACCESS
|
||||||
return norcow_update_word(key, sizeof(uint32_t) * i, val_stored[i] >> 1);
|
return norcow_update_word(key, sizeof(uint32_t) * i, val_stored[i] >> 1);
|
||||||
|
#else
|
||||||
|
return storage_set_counter(key, *count);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
return storage_set_counter(key, *count);
|
return storage_set_counter(key, *count);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ export ASAN_OPTIONS=verify_asan_link_order=0
|
|||||||
|
|
||||||
build:
|
build:
|
||||||
$(MAKE) -C c
|
$(MAKE) -C c
|
||||||
|
$(MAKE) -C c libtrezor-storage-qw.so
|
||||||
$(MAKE) -C c0
|
$(MAKE) -C c0
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
@ -28,19 +28,32 @@ SRC += crypto/sha2.c
|
|||||||
SRC += crypto/memzero.c
|
SRC += crypto/memzero.c
|
||||||
|
|
||||||
OBJ = $(SRC:%.c=build/%.o)
|
OBJ = $(SRC:%.c=build/%.o)
|
||||||
|
OBJ_QW = $(SRC:%.c=build_qw/%.o)
|
||||||
|
|
||||||
OUT = libtrezor-storage.so
|
OUT = libtrezor-storage.so
|
||||||
|
OUT_QW = libtrezor-storage-qw.so
|
||||||
|
|
||||||
$(OUT): $(OBJ)
|
$(OUT): $(OBJ)
|
||||||
$(CC) $(CFLAGS) $(LIBS) $(OBJ) -shared -o $(OUT)
|
$(CC) $(CFLAGS) $(LIBS) $(OBJ) -shared -o $(OUT)
|
||||||
|
|
||||||
|
$(OUT_QW): $(OBJ_QW)
|
||||||
|
$(CC) $(CFLAGS) -DFLASH_QUADWORD $(LIBS) $(OBJ_QW) -shared -o $(OUT_QW)
|
||||||
|
|
||||||
build/crypto/chacha20poly1305/chacha_merged.o: $(BASE)crypto/chacha20poly1305/chacha_merged.c
|
build/crypto/chacha20poly1305/chacha_merged.o: $(BASE)crypto/chacha20poly1305/chacha_merged.c
|
||||||
mkdir -p $(@D)
|
mkdir -p $(@D)
|
||||||
|
|
||||||
|
$(CC) $(CFLAGS) $(INC) -c $< -o $@
|
||||||
|
build_qw/crypto/chacha20poly1305/chacha_merged.o: $(BASE)crypto/chacha20poly1305/chacha_merged.c
|
||||||
|
mkdir -p $(@D)
|
||||||
$(CC) $(CFLAGS) $(INC) -c $< -o $@
|
$(CC) $(CFLAGS) $(INC) -c $< -o $@
|
||||||
|
|
||||||
build/%.o: $(BASE)%.c $(BASE)%.h
|
build/%.o: $(BASE)%.c $(BASE)%.h
|
||||||
mkdir -p $(@D)
|
mkdir -p $(@D)
|
||||||
$(CC) $(CFLAGS) $(INC) -c $< -o $@
|
$(CC) $(CFLAGS) $(INC) -c $< -o $@
|
||||||
|
|
||||||
|
build_qw/%.o: $(BASE)%.c $(BASE)%.h
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -DFLASH_QUADWORD $(INC) -c $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OUT) $(OBJ)
|
rm -f $(OUT) $(OBJ)
|
||||||
|
@ -24,7 +24,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "secbool.h"
|
#include "secbool.h"
|
||||||
|
|
||||||
|
#ifndef FLASH_QUADWORD
|
||||||
#define FLASH_BYTE_ACCESS 1
|
#define FLASH_BYTE_ACCESS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "flash_common.h"
|
#include "flash_common.h"
|
||||||
#include "test_layout.h"
|
#include "test_layout.h"
|
||||||
|
@ -4,11 +4,12 @@ import os
|
|||||||
EXTERNAL_SALT_LEN = 32
|
EXTERNAL_SALT_LEN = 32
|
||||||
sectrue = -1431655766 # 0xAAAAAAAAA
|
sectrue = -1431655766 # 0xAAAAAAAAA
|
||||||
fname = os.path.join(os.path.dirname(__file__), "libtrezor-storage.so")
|
fname = os.path.join(os.path.dirname(__file__), "libtrezor-storage.so")
|
||||||
|
fname_qw = os.path.join(os.path.dirname(__file__), "libtrezor-storage-qw.so")
|
||||||
|
|
||||||
|
|
||||||
class Storage:
|
class Storage:
|
||||||
def __init__(self) -> None:
|
def __init__(self, flash_byte_access=True) -> None:
|
||||||
self.lib = c.cdll.LoadLibrary(fname)
|
self.lib = c.cdll.LoadLibrary(fname if flash_byte_access else fname_qw)
|
||||||
self.flash_size = c.cast(self.lib.FLASH_SIZE, c.POINTER(c.c_uint32))[0]
|
self.flash_size = c.cast(self.lib.FLASH_SIZE, c.POINTER(c.c_uint32))[0]
|
||||||
self.flash_buffer = c.create_string_buffer(self.flash_size)
|
self.flash_buffer = c.create_string_buffer(self.flash_size)
|
||||||
c.cast(self.lib.FLASH_BUFFER, c.POINTER(c.c_void_p))[0] = c.addressof(
|
c.cast(self.lib.FLASH_BUFFER, c.POINTER(c.c_void_p))[0] = c.addressof(
|
||||||
|
@ -120,6 +120,7 @@ POLY1305_MAC_SIZE = 16
|
|||||||
|
|
||||||
# The length of the ChaCha20 IV (aka nonce) in bytes as per RFC 7539.
|
# The length of the ChaCha20 IV (aka nonce) in bytes as per RFC 7539.
|
||||||
CHACHA_IV_SIZE = 12
|
CHACHA_IV_SIZE = 12
|
||||||
|
CHACHA_IV_PADDING = 4
|
||||||
|
|
||||||
# ----- Norcow ----- #
|
# ----- Norcow ----- #
|
||||||
|
|
||||||
|
@ -4,31 +4,43 @@ from struct import pack
|
|||||||
from . import consts
|
from . import consts
|
||||||
|
|
||||||
|
|
||||||
def align4_int(i: int):
|
def align_int(i: int, align: int):
|
||||||
return (4 - i) % 4
|
return (align - i) % align
|
||||||
|
|
||||||
|
|
||||||
def align4_data(data):
|
def align_data(data, align: int):
|
||||||
return data + b"\x00" * align4_int(len(data))
|
return data + b"\x00" * align_int(len(data), align)
|
||||||
|
|
||||||
|
|
||||||
class Norcow:
|
class Norcow:
|
||||||
def __init__(self):
|
def __init__(self, flash_byte_access=True):
|
||||||
self.sectors = None
|
self.sectors = None
|
||||||
self.active_sector = 0
|
self.active_sector = 0
|
||||||
|
self.flash_byte_access = flash_byte_access
|
||||||
|
if flash_byte_access:
|
||||||
|
self.word_size = consts.WORD_SIZE
|
||||||
|
self.magic = consts.NORCOW_MAGIC_AND_VERSION
|
||||||
|
self.item_prefix_len = 4
|
||||||
|
else:
|
||||||
|
self.word_size = 4 * consts.WORD_SIZE
|
||||||
|
self.magic = consts.NORCOW_MAGIC_AND_VERSION + bytes([0xFF] * 8)
|
||||||
|
self.item_prefix_len = 2 * 4 * consts.WORD_SIZE
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
if self.sectors:
|
if self.sectors:
|
||||||
for sector in range(consts.NORCOW_SECTOR_COUNT):
|
for sector in range(consts.NORCOW_SECTOR_COUNT):
|
||||||
if self.sectors[sector][:8] == consts.NORCOW_MAGIC_AND_VERSION:
|
if self.sectors[sector][: len(self.magic)] == self.magic:
|
||||||
self.active_sector = sector
|
self.active_sector = sector
|
||||||
self.active_offset = self.find_free_offset()
|
self.active_offset = self.find_free_offset()
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.wipe()
|
self.wipe()
|
||||||
|
|
||||||
|
def is_byte_access(self):
|
||||||
|
return self.flash_byte_access
|
||||||
|
|
||||||
def find_free_offset(self):
|
def find_free_offset(self):
|
||||||
offset = len(consts.NORCOW_MAGIC_AND_VERSION)
|
offset = len(self.magic)
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
k, v = self._read_item(offset)
|
k, v = self._read_item(offset)
|
||||||
@ -45,9 +57,9 @@ class Norcow:
|
|||||||
bytearray([0xFF] * consts.NORCOW_SECTOR_SIZE)
|
bytearray([0xFF] * consts.NORCOW_SECTOR_SIZE)
|
||||||
for _ in range(consts.NORCOW_SECTOR_COUNT)
|
for _ in range(consts.NORCOW_SECTOR_COUNT)
|
||||||
]
|
]
|
||||||
self.sectors[sector][:8] = consts.NORCOW_MAGIC_AND_VERSION
|
self.sectors[sector][: len(self.magic)] = self.magic
|
||||||
self.active_sector = sector
|
self.active_sector = sector
|
||||||
self.active_offset = len(consts.NORCOW_MAGIC_AND_VERSION)
|
self.active_offset = len(self.magic)
|
||||||
|
|
||||||
def get(self, key: int) -> bytes:
|
def get(self, key: int) -> bytes:
|
||||||
value, _ = self._find_item(key)
|
value, _ = self._find_item(key)
|
||||||
@ -65,7 +77,10 @@ class Norcow:
|
|||||||
else:
|
else:
|
||||||
self._delete_old(pos, found_value)
|
self._delete_old(pos, found_value)
|
||||||
|
|
||||||
if self.active_offset + 4 + len(val) > consts.NORCOW_SECTOR_SIZE:
|
if (
|
||||||
|
self.active_offset + self.item_prefix_len + len(val)
|
||||||
|
> consts.NORCOW_SECTOR_SIZE
|
||||||
|
):
|
||||||
self._compact()
|
self._compact()
|
||||||
|
|
||||||
self._append(key, val)
|
self._append(key, val)
|
||||||
@ -95,15 +110,20 @@ class Norcow:
|
|||||||
Item is updatable if the new value is the same or
|
Item is updatable if the new value is the same or
|
||||||
it changes 1 to 0 only (the flash memory does not
|
it changes 1 to 0 only (the flash memory does not
|
||||||
allow to flip 0 to 1 unless you wipe it).
|
allow to flip 0 to 1 unless you wipe it).
|
||||||
|
|
||||||
|
For flash with no byte access, item is updatable if the new value is the same
|
||||||
"""
|
"""
|
||||||
if len(old) != len(new):
|
if len(old) != len(new):
|
||||||
return False
|
return False
|
||||||
if old == new:
|
if old == new:
|
||||||
return True
|
return True
|
||||||
for a, b in zip(old, new):
|
if self.flash_byte_access:
|
||||||
if a & b != b:
|
for a, b in zip(old, new):
|
||||||
return False
|
if a & b != b:
|
||||||
return True
|
return False
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def _delete_old(self, pos: int, value: bytes):
|
def _delete_old(self, pos: int, value: bytes):
|
||||||
wiped_data = b"\x00" * len(value)
|
wiped_data = b"\x00" * len(value)
|
||||||
@ -113,14 +133,49 @@ class Norcow:
|
|||||||
self.active_offset += self._write(self.active_offset, key, value)
|
self.active_offset += self._write(self.active_offset, key, value)
|
||||||
|
|
||||||
def _write(self, pos: int, key: int, new_value: bytes) -> int:
|
def _write(self, pos: int, key: int, new_value: bytes) -> int:
|
||||||
data = pack("<HH", key, len(new_value)) + align4_data(new_value)
|
if self.flash_byte_access:
|
||||||
if pos + len(data) > consts.NORCOW_SECTOR_SIZE:
|
data = pack("<HH", key, len(new_value)) + align_data(
|
||||||
raise RuntimeError("Norcow: item too big")
|
new_value, self.word_size
|
||||||
self.sectors[self.active_sector][pos : pos + len(data)] = data
|
)
|
||||||
return len(data)
|
if pos + len(data) > consts.NORCOW_SECTOR_SIZE:
|
||||||
|
raise RuntimeError("Norcow: item too big")
|
||||||
|
self.sectors[self.active_sector][pos : pos + len(data)] = data
|
||||||
|
return len(data)
|
||||||
|
else:
|
||||||
|
if len(new_value) <= 12:
|
||||||
|
if key == 0:
|
||||||
|
self.sectors[self.active_sector][pos : pos + self.word_size] = [
|
||||||
|
0
|
||||||
|
] * self.word_size
|
||||||
|
else:
|
||||||
|
if len(new_value) == 0:
|
||||||
|
data = pack("<HH", len(new_value), key) + bytes([0] * 12)
|
||||||
|
else:
|
||||||
|
data = pack("<HH", len(new_value), key) + align_data(
|
||||||
|
new_value, 12
|
||||||
|
)
|
||||||
|
if pos + len(data) > consts.NORCOW_SECTOR_SIZE:
|
||||||
|
raise RuntimeError("Norcow: item too big")
|
||||||
|
self.sectors[self.active_sector][pos : pos + self.word_size] = data
|
||||||
|
return len(data)
|
||||||
|
else:
|
||||||
|
data = []
|
||||||
|
data += pack("<L", len(new_value))
|
||||||
|
data += bytes([0xFF] * 12)
|
||||||
|
if key == 0:
|
||||||
|
data += bytes([0] * 16)
|
||||||
|
else:
|
||||||
|
data += pack("<L", key)
|
||||||
|
data += bytes([0xFF] * 12)
|
||||||
|
data += align_data(new_value, self.word_size)
|
||||||
|
|
||||||
|
if pos + len(data) > consts.NORCOW_SECTOR_SIZE:
|
||||||
|
raise RuntimeError("Norcow: item too big")
|
||||||
|
self.sectors[self.active_sector][pos : pos + len(data)] = data
|
||||||
|
return len(data)
|
||||||
|
|
||||||
def _find_item(self, key: int) -> (bytes, int):
|
def _find_item(self, key: int) -> (bytes, int):
|
||||||
offset = len(consts.NORCOW_MAGIC_AND_VERSION)
|
offset = len(self.magic)
|
||||||
value = False
|
value = False
|
||||||
pos = offset
|
pos = offset
|
||||||
while True:
|
while True:
|
||||||
@ -135,7 +190,7 @@ class Norcow:
|
|||||||
return value, pos
|
return value, pos
|
||||||
|
|
||||||
def _get_all_keys(self) -> (bytes, int):
|
def _get_all_keys(self) -> (bytes, int):
|
||||||
offset = len(consts.NORCOW_MAGIC_AND_VERSION)
|
offset = len(self.magic)
|
||||||
keys = set()
|
keys = set()
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -147,21 +202,53 @@ class Norcow:
|
|||||||
return keys
|
return keys
|
||||||
|
|
||||||
def _norcow_item_length(self, data: bytes) -> int:
|
def _norcow_item_length(self, data: bytes) -> int:
|
||||||
# APP_ID, KEY_ID, LENGTH, DATA, ALIGNMENT
|
if len(data) <= 12 and not self.flash_byte_access:
|
||||||
return 1 + 1 + 2 + len(data) + align4_int(len(data))
|
return self.word_size
|
||||||
|
else:
|
||||||
|
# APP_ID, KEY_ID, LENGTH, DATA, ALIGNMENT
|
||||||
|
return (
|
||||||
|
self.item_prefix_len + len(data) + align_int(len(data), self.word_size)
|
||||||
|
)
|
||||||
|
|
||||||
def _read_item(self, offset: int) -> (int, bytes):
|
def _read_item(self, offset: int) -> (int, bytes):
|
||||||
key = self.sectors[self.active_sector][offset : offset + 2]
|
if offset >= consts.NORCOW_SECTOR_SIZE:
|
||||||
key = int.from_bytes(key, sys.byteorder)
|
|
||||||
if key == consts.NORCOW_KEY_FREE:
|
|
||||||
raise ValueError("Norcow: no data on this offset")
|
raise ValueError("Norcow: no data on this offset")
|
||||||
length = self.sectors[self.active_sector][offset + 2 : offset + 4]
|
|
||||||
length = int.from_bytes(length, sys.byteorder)
|
if self.flash_byte_access:
|
||||||
value = self.sectors[self.active_sector][offset + 4 : offset + 4 + length]
|
key = self.sectors[self.active_sector][offset : offset + 2]
|
||||||
|
key = int.from_bytes(key, sys.byteorder)
|
||||||
|
if key == consts.NORCOW_KEY_FREE:
|
||||||
|
raise ValueError("Norcow: no data on this offset")
|
||||||
|
length = self.sectors[self.active_sector][offset + 2 : offset + 4]
|
||||||
|
length = int.from_bytes(length, sys.byteorder)
|
||||||
|
value = self.sectors[self.active_sector][offset + 4 : offset + 4 + length]
|
||||||
|
else:
|
||||||
|
|
||||||
|
length = self.sectors[self.active_sector][offset : offset + 2]
|
||||||
|
length = int.from_bytes(length, sys.byteorder)
|
||||||
|
|
||||||
|
if length <= 12:
|
||||||
|
key = self.sectors[self.active_sector][offset + 2 : offset + 4]
|
||||||
|
key = int.from_bytes(key, sys.byteorder)
|
||||||
|
if key == consts.NORCOW_KEY_FREE:
|
||||||
|
raise ValueError("Norcow: no data on this offset")
|
||||||
|
value = self.sectors[self.active_sector][
|
||||||
|
offset + 4 : offset + 4 + length
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
key = self.sectors[self.active_sector][
|
||||||
|
offset + self.word_size : offset + self.word_size + 2
|
||||||
|
]
|
||||||
|
key = int.from_bytes(key, sys.byteorder)
|
||||||
|
if key == consts.NORCOW_KEY_FREE:
|
||||||
|
raise ValueError("Norcow: no data on this offset")
|
||||||
|
value = self.sectors[self.active_sector][
|
||||||
|
offset + 2 * self.word_size : offset + 2 * self.word_size + length
|
||||||
|
]
|
||||||
return key, value
|
return key, value
|
||||||
|
|
||||||
def _compact(self):
|
def _compact(self):
|
||||||
offset = len(consts.NORCOW_MAGIC_AND_VERSION)
|
offset = len(self.magic)
|
||||||
data = list()
|
data = list()
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
@ -117,7 +117,10 @@ class PinLog:
|
|||||||
+ helpers.to_bytes_by_words(pin_success_log, consts.PIN_LOG_SIZE)
|
+ helpers.to_bytes_by_words(pin_success_log, consts.PIN_LOG_SIZE)
|
||||||
+ helpers.to_bytes_by_words(pin_entry_log, consts.PIN_LOG_SIZE)
|
+ helpers.to_bytes_by_words(pin_entry_log, consts.PIN_LOG_SIZE)
|
||||||
)
|
)
|
||||||
try:
|
if self.norcow.is_byte_access():
|
||||||
self.norcow.replace(consts.PIN_LOG_KEY, pin_log)
|
try:
|
||||||
except RuntimeError:
|
self.norcow.replace(consts.PIN_LOG_KEY, pin_log)
|
||||||
|
except RuntimeError:
|
||||||
|
self.norcow.set(consts.PIN_LOG_KEY, pin_log)
|
||||||
|
else:
|
||||||
self.norcow.set(consts.PIN_LOG_KEY, pin_log)
|
self.norcow.set(consts.PIN_LOG_KEY, pin_log)
|
||||||
|
@ -7,12 +7,12 @@ from .pin_log import PinLog
|
|||||||
|
|
||||||
|
|
||||||
class Storage:
|
class Storage:
|
||||||
def __init__(self):
|
def __init__(self, flash_byte_access: bool = True):
|
||||||
self.initialized = False
|
self.initialized = False
|
||||||
self.unlocked = False
|
self.unlocked = False
|
||||||
self.dek = None
|
self.dek = None
|
||||||
self.sak = None
|
self.sak = None
|
||||||
self.nc = Norcow()
|
self.nc = Norcow(flash_byte_access=flash_byte_access)
|
||||||
self.pin_log = PinLog(self.nc)
|
self.pin_log = PinLog(self.nc)
|
||||||
|
|
||||||
def init(self, hardware_salt: bytes = b""):
|
def init(self, hardware_salt: bytes = b""):
|
||||||
@ -153,9 +153,10 @@ class Storage:
|
|||||||
if val > consts.UINT32_MAX:
|
if val > consts.UINT32_MAX:
|
||||||
raise RuntimeError("Failed to set value in storage.")
|
raise RuntimeError("Failed to set value in storage.")
|
||||||
|
|
||||||
counter = val.to_bytes(4, sys.byteorder) + bytearray(
|
counter = val.to_bytes(4, sys.byteorder)
|
||||||
b"\xFF" * consts.COUNTER_TAIL_SIZE
|
|
||||||
)
|
if self.nc.is_byte_access():
|
||||||
|
counter += bytearray(b"\xFF" * consts.COUNTER_TAIL_SIZE)
|
||||||
self.set(key, counter)
|
self.set(key, counter)
|
||||||
|
|
||||||
def next_counter(self, key: int) -> int:
|
def next_counter(self, key: int) -> int:
|
||||||
@ -168,21 +169,30 @@ class Storage:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
base = int.from_bytes(current[:4], sys.byteorder)
|
base = int.from_bytes(current[:4], sys.byteorder)
|
||||||
tail = helpers.to_int_by_words(current[4:])
|
|
||||||
tail_count = f"{tail:064b}".count("0")
|
|
||||||
increased_count = base + tail_count + 1
|
|
||||||
if increased_count > consts.UINT32_MAX:
|
|
||||||
raise RuntimeError("Failed to set value in storage.")
|
|
||||||
|
|
||||||
if tail_count == consts.COUNTER_MAX_TAIL:
|
if self.nc.is_byte_access():
|
||||||
|
base = int.from_bytes(current[:4], sys.byteorder)
|
||||||
|
tail = helpers.to_int_by_words(current[4:])
|
||||||
|
tail_count = f"{tail:064b}".count("0")
|
||||||
|
increased_count = base + tail_count + 1
|
||||||
|
if increased_count > consts.UINT32_MAX:
|
||||||
|
raise RuntimeError("Failed to set value in storage.")
|
||||||
|
|
||||||
|
if tail_count == consts.COUNTER_MAX_TAIL:
|
||||||
|
self.set_counter(key, increased_count)
|
||||||
|
return increased_count
|
||||||
|
|
||||||
|
self.set(
|
||||||
|
key,
|
||||||
|
current[:4]
|
||||||
|
+ helpers.to_bytes_by_words(tail >> 1, consts.COUNTER_TAIL_SIZE),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
increased_count = base + 1
|
||||||
|
if increased_count > consts.UINT32_MAX:
|
||||||
|
raise RuntimeError("Failed to set value in storage.")
|
||||||
|
|
||||||
self.set_counter(key, increased_count)
|
self.set_counter(key, increased_count)
|
||||||
return increased_count
|
|
||||||
|
|
||||||
self.set(
|
|
||||||
key,
|
|
||||||
current[:4]
|
|
||||||
+ helpers.to_bytes_by_words(tail >> 1, consts.COUNTER_TAIL_SIZE),
|
|
||||||
)
|
|
||||||
return increased_count
|
return increased_count
|
||||||
|
|
||||||
def delete(self, key: int) -> bool:
|
def delete(self, key: int) -> bool:
|
||||||
@ -214,19 +224,32 @@ class Storage:
|
|||||||
data = self.nc.get(key)
|
data = self.nc.get(key)
|
||||||
iv = data[: consts.CHACHA_IV_SIZE]
|
iv = data[: consts.CHACHA_IV_SIZE]
|
||||||
# cipher text with MAC
|
# cipher text with MAC
|
||||||
|
|
||||||
|
padding = 0
|
||||||
|
if not self.nc.is_byte_access():
|
||||||
|
padding = consts.CHACHA_IV_PADDING
|
||||||
|
|
||||||
tag = data[
|
tag = data[
|
||||||
consts.CHACHA_IV_SIZE : consts.CHACHA_IV_SIZE + consts.POLY1305_MAC_SIZE
|
consts.CHACHA_IV_SIZE
|
||||||
|
+ padding : consts.CHACHA_IV_SIZE
|
||||||
|
+ padding
|
||||||
|
+ consts.POLY1305_MAC_SIZE
|
||||||
]
|
]
|
||||||
ciphertext = data[consts.CHACHA_IV_SIZE + consts.POLY1305_MAC_SIZE :]
|
ciphertext = data[consts.CHACHA_IV_SIZE + padding + consts.POLY1305_MAC_SIZE :]
|
||||||
return crypto.chacha_poly_decrypt(
|
return crypto.chacha_poly_decrypt(
|
||||||
self.dek, key, iv, ciphertext + tag, key.to_bytes(2, sys.byteorder)
|
self.dek, key, iv, ciphertext + tag, key.to_bytes(2, sys.byteorder)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _set_encrypt(self, key: int, val: bytes):
|
def _set_encrypt(self, key: int, val: bytes):
|
||||||
|
|
||||||
|
padding = 0
|
||||||
|
if not self.nc.is_byte_access():
|
||||||
|
padding = consts.CHACHA_IV_PADDING
|
||||||
|
|
||||||
# In C, data are preallocated beforehand for encrypted values,
|
# In C, data are preallocated beforehand for encrypted values,
|
||||||
# to match the behaviour we do the same.
|
# to match the behaviour we do the same.
|
||||||
preallocate = b"\xFF" * (
|
preallocate = b"\xFF" * (
|
||||||
consts.CHACHA_IV_SIZE + len(val) + consts.POLY1305_MAC_SIZE
|
consts.CHACHA_IV_SIZE + padding + len(val) + consts.POLY1305_MAC_SIZE
|
||||||
)
|
)
|
||||||
self.nc.set(key, preallocate)
|
self.nc.set(key, preallocate)
|
||||||
if consts.is_app_protected(key >> 8):
|
if consts.is_app_protected(key >> 8):
|
||||||
@ -234,10 +257,11 @@ class Storage:
|
|||||||
self.nc.set(consts.SAT_KEY, sat)
|
self.nc.set(consts.SAT_KEY, sat)
|
||||||
|
|
||||||
iv = prng.random_buffer(consts.CHACHA_IV_SIZE)
|
iv = prng.random_buffer(consts.CHACHA_IV_SIZE)
|
||||||
|
padding = bytes([0x00] * padding)
|
||||||
cipher_text, tag = crypto.chacha_poly_encrypt(
|
cipher_text, tag = crypto.chacha_poly_encrypt(
|
||||||
self.dek, iv, val, key.to_bytes(2, sys.byteorder)
|
self.dek, iv, val, key.to_bytes(2, sys.byteorder)
|
||||||
)
|
)
|
||||||
return self.nc.replace(key, iv + tag + cipher_text)
|
return self.nc.replace(key, iv + padding + tag + cipher_text)
|
||||||
|
|
||||||
def _calculate_authentication_tag(self) -> bytes:
|
def _calculate_authentication_tag(self) -> bytes:
|
||||||
keys = []
|
keys = []
|
||||||
|
305
storage/tests/python/tests/test_norcow_qw.py
Normal file
305
storage/tests/python/tests/test_norcow_qw.py
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from ..src import consts, norcow
|
||||||
|
from . import common
|
||||||
|
|
||||||
|
|
||||||
|
def test_norcow_set_qw():
|
||||||
|
n = norcow.Norcow(flash_byte_access=False)
|
||||||
|
n.init()
|
||||||
|
n.set(0x0001, b"123")
|
||||||
|
data = n._dump()[0][:256]
|
||||||
|
assert data[:16] == consts.NORCOW_MAGIC_AND_VERSION + b"\xff" * 8
|
||||||
|
assert data[16:18] == b"\x03\x00" # length
|
||||||
|
assert data[18:20] == b"\x01\x00" # app + key
|
||||||
|
assert data[20:23] == b"123" # data
|
||||||
|
assert data[23:32] == bytes([0] * 9) # alignment
|
||||||
|
assert common.all_ff_bytes(data[32:])
|
||||||
|
|
||||||
|
n.wipe()
|
||||||
|
n.set(0x0901, b"hello")
|
||||||
|
data = n._dump()[0][:256]
|
||||||
|
assert data[:16] == consts.NORCOW_MAGIC_AND_VERSION + b"\xff" * 8
|
||||||
|
assert data[16:18] == b"\x05\x00" # length\x00
|
||||||
|
assert data[18:20] == b"\x01\x09" # app + key
|
||||||
|
assert data[20:25] == b"hello" # data
|
||||||
|
assert data[25:32] == bytes([0] * 7) # alignment
|
||||||
|
assert common.all_ff_bytes(data[32:])
|
||||||
|
|
||||||
|
offset = 32
|
||||||
|
n.set(0x0102, b"world!")
|
||||||
|
data = n._dump()[0][:256]
|
||||||
|
assert data[offset : offset + 2] == b"\x06\x00" # length
|
||||||
|
assert data[offset + 2 : offset + 4] == b"\x02\x01" # app + key
|
||||||
|
assert data[offset + 4 : offset + 4 + 6] == b"world!" # data
|
||||||
|
assert data[offset + 4 + 6 : offset + 16] == bytes([0] * 6) # alignment
|
||||||
|
assert common.all_ff_bytes(data[offset + 16 :])
|
||||||
|
|
||||||
|
|
||||||
|
def test_norcow_set_qw_long():
|
||||||
|
n = norcow.Norcow(flash_byte_access=False)
|
||||||
|
n.init()
|
||||||
|
n.set(0x0001, b"1234567890abc")
|
||||||
|
data = n._dump()[0][:256]
|
||||||
|
assert data[:16] == consts.NORCOW_MAGIC_AND_VERSION + b"\xff" * 8
|
||||||
|
assert data[16:18] == b"\x0D\x00" # length
|
||||||
|
assert data[32:34] == b"\x01\x00" # app + key
|
||||||
|
assert data[48:61] == b"1234567890abc" # data
|
||||||
|
assert common.all_ff_bytes(data[64:])
|
||||||
|
|
||||||
|
n.wipe()
|
||||||
|
n.set(0x0901, b"hello_hello__")
|
||||||
|
data = n._dump()[0][:256]
|
||||||
|
assert data[:16] == consts.NORCOW_MAGIC_AND_VERSION + b"\xff" * 8
|
||||||
|
assert data[16:18] == b"\x0D\x00" # length\x00
|
||||||
|
assert data[32:34] == b"\x01\x09" # app + key
|
||||||
|
assert data[48:61] == b"hello_hello__" # data
|
||||||
|
assert data[61:64] == b"\x00\x00\x00" # alignment
|
||||||
|
assert common.all_ff_bytes(data[64:])
|
||||||
|
|
||||||
|
offset = 64
|
||||||
|
n.set(0x0102, b"world!_world!")
|
||||||
|
data = n._dump()[0][:256]
|
||||||
|
assert data[offset : offset + 2] == b"\x0D\x00" # length
|
||||||
|
assert data[offset + 16 : offset + 18] == b"\x02\x01" # app + key
|
||||||
|
assert data[offset + 32 : offset + 32 + 13] == b"world!_world!" # data
|
||||||
|
assert data[offset + 32 + 13 : offset + 48] == b"\x00\x00\x00" # alignment
|
||||||
|
assert common.all_ff_bytes(data[offset + 48 :])
|
||||||
|
|
||||||
|
|
||||||
|
def test_norcow_read_item_qw():
|
||||||
|
n = norcow.Norcow(flash_byte_access=False)
|
||||||
|
n.init()
|
||||||
|
n.set(0x0001, b"123")
|
||||||
|
n.set(0x0002, b"456")
|
||||||
|
n.set(0x0101, b"789")
|
||||||
|
key, value = n._read_item(32)
|
||||||
|
assert key == 0x0002
|
||||||
|
assert value == b"456"
|
||||||
|
key, value = n._read_item(48)
|
||||||
|
assert key == 0x0101
|
||||||
|
assert value == b"789"
|
||||||
|
|
||||||
|
with pytest.raises(ValueError) as e:
|
||||||
|
key, value = n._read_item(204)
|
||||||
|
assert "no data" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_norcow_get_item_qw():
|
||||||
|
n = norcow.Norcow(flash_byte_access=False)
|
||||||
|
n.init()
|
||||||
|
n.set(0x0001, b"123")
|
||||||
|
n.set(0x0002, b"456")
|
||||||
|
n.set(0x0101, b"789")
|
||||||
|
value = n.get(0x0001)
|
||||||
|
assert value == b"123"
|
||||||
|
assert (
|
||||||
|
n._dump()[0][:80].hex()
|
||||||
|
== consts.NORCOW_MAGIC_AND_VERSION.hex()
|
||||||
|
+ "ffffffffffffffff"
|
||||||
|
+ "03000100313233000000000000000000"
|
||||||
|
+ "03000200343536000000000000000000"
|
||||||
|
+ "03000101373839000000000000000000"
|
||||||
|
+ "ffffffffffffffffffffffffffffffff"
|
||||||
|
)
|
||||||
|
|
||||||
|
# replacing item with the same value (update)
|
||||||
|
n.set(0x0101, b"789")
|
||||||
|
value = n.get(0x0101)
|
||||||
|
assert value == b"789"
|
||||||
|
assert (
|
||||||
|
n._dump()[0][:80].hex()
|
||||||
|
== consts.NORCOW_MAGIC_AND_VERSION.hex()
|
||||||
|
+ "ffffffffffffffff"
|
||||||
|
+ "03000100313233000000000000000000"
|
||||||
|
+ "03000200343536000000000000000000"
|
||||||
|
+ "03000101373839000000000000000000"
|
||||||
|
+ "ffffffffffffffffffffffffffffffff"
|
||||||
|
)
|
||||||
|
|
||||||
|
# replacing item with value with less 1 bits than before (wipe and new entry)
|
||||||
|
n.set(0x0101, b"788")
|
||||||
|
value = n.get(0x0101)
|
||||||
|
assert value == b"788"
|
||||||
|
assert (
|
||||||
|
n._dump()[0][:96].hex()
|
||||||
|
== consts.NORCOW_MAGIC_AND_VERSION.hex()
|
||||||
|
+ "ffffffffffffffff"
|
||||||
|
+ "03000100313233000000000000000000"
|
||||||
|
+ "03000200343536000000000000000000"
|
||||||
|
+ "00000000000000000000000000000000"
|
||||||
|
+ "03000101373838000000000000000000"
|
||||||
|
+ "ffffffffffffffffffffffffffffffff"
|
||||||
|
)
|
||||||
|
|
||||||
|
# replacing item with value with more 1 bits than before (wipe and new entry)
|
||||||
|
n.set(0x0101, b"787")
|
||||||
|
value = n.get(0x0101)
|
||||||
|
assert value == b"787"
|
||||||
|
assert (
|
||||||
|
n._dump()[0][:112].hex()
|
||||||
|
== consts.NORCOW_MAGIC_AND_VERSION.hex()
|
||||||
|
+ "ffffffffffffffff"
|
||||||
|
+ "03000100313233000000000000000000"
|
||||||
|
+ "03000200343536000000000000000000"
|
||||||
|
+ "00000000000000000000000000000000"
|
||||||
|
+ "00000000000000000000000000000000"
|
||||||
|
+ "03000101373837000000000000000000"
|
||||||
|
+ "ffffffffffffffffffffffffffffffff"
|
||||||
|
)
|
||||||
|
|
||||||
|
n.set(0x0002, b"world")
|
||||||
|
n.set(0x0002, b"earth")
|
||||||
|
value = n.get(0x0002)
|
||||||
|
assert value == b"earth"
|
||||||
|
|
||||||
|
|
||||||
|
def test_norcow_get_item_qw_long():
|
||||||
|
n = norcow.Norcow(flash_byte_access=False)
|
||||||
|
n.init()
|
||||||
|
n.set(0x0001, b"1231231231231")
|
||||||
|
n.set(0x0002, b"4564564564564")
|
||||||
|
n.set(0x0101, b"7897897897897")
|
||||||
|
value = n.get(0x0001)
|
||||||
|
assert value == b"1231231231231"
|
||||||
|
assert (
|
||||||
|
n._dump()[0][:170].hex()
|
||||||
|
== consts.NORCOW_MAGIC_AND_VERSION.hex() + "ffffffffffffffff"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"01000000ffffffffffffffffffffffff"
|
||||||
|
"31323331323331323331323331000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"02000000ffffffffffffffffffffffff"
|
||||||
|
"34353634353634353634353634000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"01010000ffffffffffffffffffffffff"
|
||||||
|
"37383937383937383937383937000000"
|
||||||
|
"ffffffffffffffffffff"
|
||||||
|
)
|
||||||
|
|
||||||
|
# replacing item with the same value (update)
|
||||||
|
n.set(0x0101, b"7897897897897")
|
||||||
|
value = n.get(0x0101)
|
||||||
|
assert value == b"7897897897897"
|
||||||
|
assert (
|
||||||
|
n._dump()[0][:170].hex()
|
||||||
|
== consts.NORCOW_MAGIC_AND_VERSION.hex() + "ffffffffffffffff"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"01000000ffffffffffffffffffffffff"
|
||||||
|
"31323331323331323331323331000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"02000000ffffffffffffffffffffffff"
|
||||||
|
"34353634353634353634353634000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"01010000ffffffffffffffffffffffff"
|
||||||
|
"37383937383937383937383937000000"
|
||||||
|
"ffffffffffffffffffff"
|
||||||
|
)
|
||||||
|
|
||||||
|
# replacing item with value with less 1 bits than before (update)
|
||||||
|
n.set(0x0101, b"7887887887887")
|
||||||
|
value = n.get(0x0101)
|
||||||
|
assert value == b"7887887887887"
|
||||||
|
assert (
|
||||||
|
n._dump()[0][:218].hex()
|
||||||
|
== consts.NORCOW_MAGIC_AND_VERSION.hex() + "ffffffffffffffff"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"01000000ffffffffffffffffffffffff"
|
||||||
|
"31323331323331323331323331000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"02000000ffffffffffffffffffffffff"
|
||||||
|
"34353634353634353634353634000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"00000000000000000000000000000000"
|
||||||
|
"00000000000000000000000000000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"01010000ffffffffffffffffffffffff"
|
||||||
|
"37383837383837383837383837000000"
|
||||||
|
"ffffffffffffffffffff"
|
||||||
|
)
|
||||||
|
|
||||||
|
# replacing item with value with more 1 bits than before (wipe and new entry)
|
||||||
|
n.set(0x0101, b"7877877877877")
|
||||||
|
value = n.get(0x0101)
|
||||||
|
assert value == b"7877877877877"
|
||||||
|
assert (
|
||||||
|
n._dump()[0][:266].hex()
|
||||||
|
== consts.NORCOW_MAGIC_AND_VERSION.hex() + "ffffffffffffffff"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"01000000ffffffffffffffffffffffff"
|
||||||
|
"31323331323331323331323331000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"02000000ffffffffffffffffffffffff"
|
||||||
|
"34353634353634353634353634000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"00000000000000000000000000000000"
|
||||||
|
"00000000000000000000000000000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"00000000000000000000000000000000"
|
||||||
|
"00000000000000000000000000000000"
|
||||||
|
"0d000000ffffffffffffffffffffffff"
|
||||||
|
"01010000ffffffffffffffffffffffff"
|
||||||
|
"37383737383737383737383737000000"
|
||||||
|
"ffffffffffffffffffff"
|
||||||
|
)
|
||||||
|
|
||||||
|
n.set(0x0002, b"world")
|
||||||
|
n.set(0x0002, b"earth")
|
||||||
|
value = n.get(0x0002)
|
||||||
|
assert value == b"earth"
|
||||||
|
|
||||||
|
|
||||||
|
def test_norcow_replace_item_qw():
|
||||||
|
n = norcow.Norcow(flash_byte_access=False)
|
||||||
|
n.init()
|
||||||
|
n.set(0x0001, b"123")
|
||||||
|
n.set(0x0002, b"456")
|
||||||
|
n.set(0x0101, b"789")
|
||||||
|
value = n.get(0x0002)
|
||||||
|
assert value == b"456"
|
||||||
|
|
||||||
|
n.replace(0x0001, b"000")
|
||||||
|
value = n.get(0x0001)
|
||||||
|
assert value == b"000"
|
||||||
|
|
||||||
|
n.replace(0x0002, b"111")
|
||||||
|
value = n.get(0x0002)
|
||||||
|
assert value == b"111"
|
||||||
|
value = n.get(0x0001)
|
||||||
|
assert value == b"000"
|
||||||
|
value = n.get(0x0101)
|
||||||
|
assert value == b"789"
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError) as e:
|
||||||
|
n.replace(0x0001, b"00000")
|
||||||
|
assert "same length" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_norcow_compact_qw():
|
||||||
|
n = norcow.Norcow(flash_byte_access=False)
|
||||||
|
n.init()
|
||||||
|
n.set(0x0101, b"ahoj_ahoj_ahoj")
|
||||||
|
n.set(0x0101, b"a" * (consts.NORCOW_SECTOR_SIZE - 380))
|
||||||
|
n.set(0x0101, b"hello_hello__")
|
||||||
|
|
||||||
|
n.set(0x0103, b"123456789xxxx")
|
||||||
|
n.set(0x0104, b"123456789xxxx")
|
||||||
|
n.set(0x0105, b"123456789xxxx")
|
||||||
|
n.set(0x0106, b"123456789xxxx")
|
||||||
|
mem = n._dump()
|
||||||
|
assert mem[0][:16] == consts.NORCOW_MAGIC_AND_VERSION + b"\xff" * 8
|
||||||
|
assert mem[0][200:300] == b"\x00" * 100
|
||||||
|
|
||||||
|
# compact is triggered
|
||||||
|
n.set(0x0107, b"123456789xxxx")
|
||||||
|
mem = n._dump()
|
||||||
|
# assert the other sector is active
|
||||||
|
assert mem[1][:16] == consts.NORCOW_MAGIC_AND_VERSION + b"\xff" * 8
|
||||||
|
# assert the deleted item was not copied
|
||||||
|
assert mem[0][200:300] == b"\xff" * 100
|
||||||
|
|
||||||
|
n.set(0x0108, b"123456789x")
|
||||||
|
n.set(0x0109, b"123456789x")
|
||||||
|
|
||||||
|
assert n.get(0x0101) == b"hello_hello__"
|
||||||
|
assert n.get(0x0103) == b"123456789xxxx"
|
@ -7,10 +7,11 @@ test_uid = b"\x67\xce\x6a\xe8\xf7\x9b\x73\x96\x83\x88\x21\x5e"
|
|||||||
|
|
||||||
|
|
||||||
def init(
|
def init(
|
||||||
unlock: bool = False, reseed: int = 0, uid: int = test_uid
|
unlock: bool = False, reseed: int = 0, uid: int = test_uid, flash_byte_access=True
|
||||||
) -> (StorageC, StoragePy):
|
) -> (StorageC, StoragePy):
|
||||||
sc = StorageC()
|
sc = StorageC(flash_byte_access)
|
||||||
sp = StoragePy()
|
sp = StoragePy(flash_byte_access)
|
||||||
|
|
||||||
sc.lib.random_reseed(reseed)
|
sc.lib.random_reseed(reseed)
|
||||||
prng.random_reseed(reseed)
|
prng.random_reseed(reseed)
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
|
@ -6,27 +6,31 @@ from . import common
|
|||||||
|
|
||||||
|
|
||||||
def test_compact():
|
def test_compact():
|
||||||
sc, sp = common.init(unlock=True)
|
for byte_access in (
|
||||||
for s in (sc, sp):
|
True,
|
||||||
s.set(0xBEEF, b"hello")
|
False,
|
||||||
s.set(0xBEEF, b"asdasdasdasd")
|
):
|
||||||
s.set(0xBEEF, b"fsdasdasdasdasdsadasdsadasdasd")
|
sc, sp = common.init(unlock=True, flash_byte_access=byte_access)
|
||||||
s.set(0x0101, b"a" * (consts.NORCOW_SECTOR_SIZE - 600))
|
for s in (sc, sp):
|
||||||
s.set(0x03FE, b"world!")
|
s.set(0xBEEF, b"hello")
|
||||||
s.set(0x04FE, b"world!xfffffffffffffffffffffffffffff")
|
s.set(0xBEEF, b"asdasdasdasd")
|
||||||
s.set(0x05FE, b"world!affffffffffffffffffffffffffffff")
|
s.set(0xBEEF, b"fsdasdasdasdasdsadasdsadasdasd")
|
||||||
s.set(0x0101, b"s")
|
s.set(0x0101, b"a" * (consts.NORCOW_SECTOR_SIZE - 1200))
|
||||||
s.set(0x06FE, b"world!aaaaaaaaaaaaaaaaaaaaaaaaab")
|
s.set(0x03FE, b"world!")
|
||||||
s.set(0x07FE, b"worxxxxxxxxxxxxxxxxxx")
|
s.set(0x04FE, b"world!xfffffffffffffffffffffffffffff")
|
||||||
s.set(0x09EE, b"worxxxxxxxxxxxxxxxxxx")
|
s.set(0x05FE, b"world!affffffffffffffffffffffffffffff")
|
||||||
assert common.memory_equals(sc, sp)
|
s.set(0x0101, b"s")
|
||||||
|
s.set(0x06FE, b"world!aaaaaaaaaaaaaaaaaaaaaaaaab")
|
||||||
|
s.set(0x07FE, b"worxxxxxxxxxxxxxxxxxx")
|
||||||
|
s.set(0x09EE, b"worxxxxxxxxxxxxxxxxxx")
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
sc, sp = common.init(unlock=True)
|
sc, sp = common.init(unlock=True, flash_byte_access=byte_access)
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
s.set(0xBEEF, b"asdasdasdasd")
|
s.set(0xBEEF, b"asdasdasdasd")
|
||||||
s.set(0xBEEF, b"fsdasdasdasdasdsadasdsadasdasd")
|
s.set(0xBEEF, b"fsdasdasdasdasdsadasdsadasdasd")
|
||||||
s.set(0x8101, b"a" * (consts.NORCOW_SECTOR_SIZE - 1000))
|
s.set(0x8101, b"a" * (consts.NORCOW_SECTOR_SIZE - 1000))
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
s.set(0x0101, b"a" * (consts.NORCOW_SECTOR_SIZE - 100))
|
s.set(0x0101, b"a" * (consts.NORCOW_SECTOR_SIZE - 100))
|
||||||
s.set(0x0101, b"hello")
|
s.set(0x0101, b"hello")
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
@ -6,61 +6,69 @@ from . import common
|
|||||||
|
|
||||||
|
|
||||||
def test_init_pin():
|
def test_init_pin():
|
||||||
sc, sp = common.init(uid=b"\x00\x00\x00\x00\x00\x00")
|
for byte_access in (True, False):
|
||||||
assert common.memory_equals(sc, sp)
|
sc, sp = common.init(
|
||||||
|
uid=b"\x00\x00\x00\x00\x00\x00", flash_byte_access=byte_access
|
||||||
|
)
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
sc, sp = common.init(uid=b"\x22\x00\xDD\x00\x00\xBE")
|
sc, sp = common.init(
|
||||||
assert common.memory_equals(sc, sp)
|
uid=b"\x22\x00\xDD\x00\x00\xBE", flash_byte_access=byte_access
|
||||||
|
)
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
|
|
||||||
def test_change_pin():
|
def test_change_pin():
|
||||||
sc, sp = common.init(unlock=True)
|
for byte_access in (True, False):
|
||||||
for s in (sc, sp):
|
sc, sp = common.init(unlock=True, flash_byte_access=byte_access)
|
||||||
assert s.change_pin("", "222")
|
for s in (sc, sp):
|
||||||
assert not s.change_pin("9999", "") # invalid PIN
|
assert s.change_pin("", "222")
|
||||||
assert s.unlock("222")
|
assert not s.change_pin("9999", "") # invalid PIN
|
||||||
assert s.change_pin("222", "99999")
|
assert s.unlock("222")
|
||||||
assert s.change_pin("99999", "Trezor")
|
assert s.change_pin("222", "99999")
|
||||||
assert s.unlock("Trezor")
|
assert s.change_pin("99999", "Trezor")
|
||||||
assert not s.unlock("9999") # invalid PIN
|
assert s.unlock("Trezor")
|
||||||
assert not s.unlock("99999") # invalid old PIN
|
assert not s.unlock("9999") # invalid PIN
|
||||||
|
assert not s.unlock("99999") # invalid old PIN
|
||||||
|
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
|
|
||||||
def test_has_pin():
|
def test_has_pin():
|
||||||
sc, sp = common.init()
|
for byte_access in (True, False):
|
||||||
for s in (sc, sp):
|
sc, sp = common.init(flash_byte_access=byte_access)
|
||||||
assert not s.has_pin()
|
for s in (sc, sp):
|
||||||
assert s.unlock("")
|
assert not s.has_pin()
|
||||||
assert not s.has_pin()
|
assert s.unlock("")
|
||||||
assert s.change_pin("", "22")
|
assert not s.has_pin()
|
||||||
assert s.has_pin()
|
assert s.change_pin("", "22")
|
||||||
assert s.change_pin("22", "")
|
assert s.has_pin()
|
||||||
assert not s.has_pin()
|
assert s.change_pin("22", "")
|
||||||
|
assert not s.has_pin()
|
||||||
|
|
||||||
|
|
||||||
def test_wipe_after_max_pin():
|
def test_wipe_after_max_pin():
|
||||||
sc, sp = common.init(unlock=True)
|
for byte_access in (True, False):
|
||||||
for s in (sc, sp):
|
sc, sp = common.init(unlock=True, flash_byte_access=byte_access)
|
||||||
assert s.change_pin("", "222")
|
for s in (sc, sp):
|
||||||
assert s.unlock("222")
|
assert s.change_pin("", "222")
|
||||||
s.set(0x0202, b"Hello")
|
assert s.unlock("222")
|
||||||
|
s.set(0x0202, b"Hello")
|
||||||
|
|
||||||
# try an invalid PIN MAX - 1 times
|
# try an invalid PIN MAX - 1 times
|
||||||
for i in range(consts.PIN_MAX_TRIES - 1):
|
for i in range(consts.PIN_MAX_TRIES - 1):
|
||||||
assert not s.unlock("9999")
|
assert not s.unlock("9999")
|
||||||
# this should pass
|
# this should pass
|
||||||
assert s.unlock("222")
|
assert s.unlock("222")
|
||||||
assert s.get(0x0202) == b"Hello"
|
|
||||||
|
|
||||||
# try an invalid PIN MAX times, the storage should get wiped
|
|
||||||
for i in range(consts.PIN_MAX_TRIES):
|
|
||||||
assert not s.unlock("9999")
|
|
||||||
assert i == consts.PIN_MAX_TRIES - 1
|
|
||||||
# this should return False and raise an exception, the storage is wiped
|
|
||||||
assert not s.unlock("222")
|
|
||||||
with pytest.raises(RuntimeError):
|
|
||||||
assert s.get(0x0202) == b"Hello"
|
assert s.get(0x0202) == b"Hello"
|
||||||
|
|
||||||
assert common.memory_equals(sc, sp)
|
# try an invalid PIN MAX times, the storage should get wiped
|
||||||
|
for i in range(consts.PIN_MAX_TRIES):
|
||||||
|
assert not s.unlock("9999")
|
||||||
|
assert i == consts.PIN_MAX_TRIES - 1
|
||||||
|
# this should return False and raise an exception, the storage is wiped
|
||||||
|
assert not s.unlock("222")
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
assert s.get(0x0202) == b"Hello"
|
||||||
|
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
86
storage/tests/tests/test_random_qw.py
Normal file
86
storage/tests/tests/test_random_qw.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import hypothesis.strategies as st
|
||||||
|
from hypothesis import assume, settings
|
||||||
|
from hypothesis.stateful import Bundle, RuleBasedStateMachine, invariant, rule
|
||||||
|
|
||||||
|
from . import common
|
||||||
|
from .storage_model import StorageModel
|
||||||
|
|
||||||
|
|
||||||
|
class StorageComparison(RuleBasedStateMachine):
|
||||||
|
def __init__(self):
|
||||||
|
super(StorageComparison, self).__init__()
|
||||||
|
self.sc, self.sp = common.init(unlock=True, flash_byte_access=False)
|
||||||
|
self.sm = StorageModel()
|
||||||
|
self.sm.init(b"")
|
||||||
|
self.sm.unlock("")
|
||||||
|
self.storages = (self.sc, self.sp, self.sm)
|
||||||
|
|
||||||
|
keys = Bundle("keys")
|
||||||
|
values = Bundle("values")
|
||||||
|
pins = Bundle("pins")
|
||||||
|
|
||||||
|
@rule(target=keys, app=st.integers(1, 0xFF), key=st.integers(0, 0xFF))
|
||||||
|
def k(self, app, key):
|
||||||
|
return (app << 8) | key
|
||||||
|
|
||||||
|
@rule(target=values, v=st.binary(min_size=0, max_size=10000))
|
||||||
|
def v(self, v):
|
||||||
|
return v
|
||||||
|
|
||||||
|
@rule(target=pins, p=st.integers(1, 3))
|
||||||
|
def p(self, p):
|
||||||
|
if p == 1:
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
return str(p)
|
||||||
|
|
||||||
|
@rule(k=keys, v=values)
|
||||||
|
def set(self, k, v):
|
||||||
|
assume(k != 0xFFFF)
|
||||||
|
for s in self.storages:
|
||||||
|
s.set(k, v)
|
||||||
|
|
||||||
|
@rule(k=keys)
|
||||||
|
def delete(self, k):
|
||||||
|
assume(k != 0xFFFF)
|
||||||
|
assert len(set(s.delete(k) for s in self.storages)) == 1
|
||||||
|
|
||||||
|
@rule(p=pins)
|
||||||
|
def check_pin(self, p):
|
||||||
|
assert len(set(s.unlock(p) for s in self.storages)) == 1
|
||||||
|
self.ensure_unlocked()
|
||||||
|
|
||||||
|
@rule(oldpin=pins, newpin=pins)
|
||||||
|
def change_pin(self, oldpin, newpin):
|
||||||
|
assert len(set(s.change_pin(oldpin, newpin) for s in self.storages)) == 1
|
||||||
|
self.ensure_unlocked()
|
||||||
|
|
||||||
|
@rule()
|
||||||
|
def lock(self):
|
||||||
|
for s in self.storages:
|
||||||
|
s.lock()
|
||||||
|
self.ensure_unlocked()
|
||||||
|
|
||||||
|
@invariant()
|
||||||
|
def values_agree(self):
|
||||||
|
for k, v in self.sm:
|
||||||
|
assert self.sc.get(k) == v
|
||||||
|
|
||||||
|
@invariant()
|
||||||
|
def dumps_agree(self):
|
||||||
|
assert self.sc._dump() == self.sp._dump()
|
||||||
|
|
||||||
|
@invariant()
|
||||||
|
def pin_counters_agree(self):
|
||||||
|
assert len(set(s.get_pin_rem() for s in self.storages)) == 1
|
||||||
|
|
||||||
|
def ensure_unlocked(self):
|
||||||
|
if not self.sm.unlocked:
|
||||||
|
for s in self.storages:
|
||||||
|
assert s.unlock(self.sm.pin)
|
||||||
|
|
||||||
|
|
||||||
|
TestStorageComparison = StorageComparison.TestCase
|
||||||
|
TestStorageComparison.settings = settings(
|
||||||
|
deadline=None, max_examples=30, stateful_step_count=50
|
||||||
|
)
|
@ -14,188 +14,197 @@ chacha_strings = [
|
|||||||
|
|
||||||
|
|
||||||
def test_set_get():
|
def test_set_get():
|
||||||
sc, sp = common.init(unlock=True)
|
for byte_access in (True, False):
|
||||||
for s in (sc, sp):
|
sc, sp = common.init(unlock=True, flash_byte_access=byte_access)
|
||||||
s.set(0xBEEF, b"Hello")
|
for s in (sc, sp):
|
||||||
s.set(0xCAFE, b"world! ")
|
s.set(0xBEEF, b"Hello")
|
||||||
s.set(0xDEAD, b"How\n")
|
s.set(0xCAFE, b"world! ")
|
||||||
s.set(0xAAAA, b"are")
|
s.set(0xDEAD, b"How\n")
|
||||||
s.set(0x0901, b"you?")
|
s.set(0xAAAA, b"are")
|
||||||
s.set(0x0902, b"Lorem")
|
s.set(0x0901, b"you?")
|
||||||
s.set(0x0903, b"ipsum")
|
s.set(0x0902, b"Lorem")
|
||||||
s.set(0xDEAD, b"A\n")
|
s.set(0x0903, b"ipsum")
|
||||||
s.set(0xDEAD, b"AAAAAAAAAAA")
|
s.set(0xDEAD, b"A\n")
|
||||||
s.set(0x2200, b"BBBB")
|
s.set(0xDEAD, b"AAAAAAAAAAA")
|
||||||
assert common.memory_equals(sc, sp)
|
s.set(0x2200, b"BBBB")
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
s.change_pin("", "222")
|
s.change_pin("", "222")
|
||||||
s.change_pin("222", "99")
|
s.change_pin("222", "99")
|
||||||
s.set(0xAAAA, b"something else")
|
s.set(0xAAAA, b"something else")
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
# check data are not changed by gets
|
# check data are not changed by gets
|
||||||
datasc = sc._dump()
|
datasc = sc._dump()
|
||||||
datasp = sp._dump()
|
datasp = sp._dump()
|
||||||
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
assert s.get(0xAAAA) == b"something else"
|
assert s.get(0xAAAA) == b"something else"
|
||||||
assert s.get(0x0901) == b"you?"
|
assert s.get(0x0901) == b"you?"
|
||||||
assert s.get(0x0902) == b"Lorem"
|
assert s.get(0x0902) == b"Lorem"
|
||||||
assert s.get(0x0903) == b"ipsum"
|
assert s.get(0x0903) == b"ipsum"
|
||||||
assert s.get(0xDEAD) == b"AAAAAAAAAAA"
|
assert s.get(0xDEAD) == b"AAAAAAAAAAA"
|
||||||
assert s.get(0x2200) == b"BBBB"
|
assert s.get(0x2200) == b"BBBB"
|
||||||
|
|
||||||
assert datasc == sc._dump()
|
assert datasc == sc._dump()
|
||||||
assert datasp == sp._dump()
|
assert datasp == sp._dump()
|
||||||
|
|
||||||
# test locked storage
|
# test locked storage
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
s.lock()
|
s.lock()
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
s.set(0xAAAA, b"test public")
|
s.set(0xAAAA, b"test public")
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
s.set(0x0901, b"test protected")
|
s.set(0x0901, b"test protected")
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
s.get(0x0901)
|
s.get(0x0901)
|
||||||
assert s.get(0xAAAA) == b"something else"
|
assert s.get(0xAAAA) == b"something else"
|
||||||
|
|
||||||
# check that storage functions after unlock
|
# check that storage functions after unlock
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
s.unlock("99")
|
s.unlock("99")
|
||||||
s.set(0xAAAA, b"public")
|
s.set(0xAAAA, b"public")
|
||||||
s.set(0x0902, b"protected")
|
s.set(0x0902, b"protected")
|
||||||
assert s.get(0xAAAA) == b"public"
|
assert s.get(0xAAAA) == b"public"
|
||||||
assert s.get(0x0902) == b"protected"
|
assert s.get(0x0902) == b"protected"
|
||||||
|
|
||||||
# test delete
|
# test delete
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
assert s.delete(0x0902)
|
assert s.delete(0x0902)
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
assert not s.delete(0x7777)
|
assert not s.delete(0x7777)
|
||||||
assert not s.delete(0x0902)
|
assert not s.delete(0x0902)
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_key():
|
def test_invalid_key():
|
||||||
for s in common.init(unlock=True):
|
for byte_access in (True, False):
|
||||||
with pytest.raises(RuntimeError):
|
for s in common.init(unlock=True, flash_byte_access=byte_access):
|
||||||
s.set(0xFFFF, b"Hello")
|
with pytest.raises(RuntimeError):
|
||||||
|
s.set(0xFFFF, b"Hello")
|
||||||
|
|
||||||
|
|
||||||
def test_non_existing_key():
|
def test_non_existing_key():
|
||||||
sc, sp = common.init()
|
for byte_access in (True, False):
|
||||||
for s in (sc, sp):
|
sc, sp = common.init(flash_byte_access=byte_access)
|
||||||
with pytest.raises(RuntimeError):
|
for s in (sc, sp):
|
||||||
s.get(0xABCD)
|
with pytest.raises(RuntimeError):
|
||||||
|
s.get(0xABCD)
|
||||||
|
|
||||||
|
|
||||||
def test_chacha_strings():
|
def test_chacha_strings():
|
||||||
sc, sp = common.init(unlock=True)
|
for byte_access in (True, False):
|
||||||
for s in (sc, sp):
|
sc, sp = common.init(unlock=True, flash_byte_access=byte_access)
|
||||||
for i, string in enumerate(chacha_strings):
|
for s in (sc, sp):
|
||||||
s.set(0x0301 + i, string)
|
for i, string in enumerate(chacha_strings):
|
||||||
assert common.memory_equals(sc, sp)
|
s.set(0x0301 + i, string)
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
for i, string in enumerate(chacha_strings):
|
for i, string in enumerate(chacha_strings):
|
||||||
assert s.get(0x0301 + i) == string
|
assert s.get(0x0301 + i) == string
|
||||||
|
|
||||||
|
|
||||||
def test_set_repeated():
|
def test_set_repeated():
|
||||||
test_strings = [[0x0501, b""], [0x0502, b"test"], [0x8501, b""], [0x8502, b"test"]]
|
test_strings = [[0x0501, b""], [0x0502, b"test"], [0x8501, b""], [0x8502, b"test"]]
|
||||||
sc, sp = common.init(unlock=True)
|
|
||||||
for s in (sc, sp):
|
|
||||||
for key, val in test_strings:
|
|
||||||
s.set(key, val)
|
|
||||||
s.set(key, val)
|
|
||||||
assert common.memory_equals(sc, sp)
|
|
||||||
|
|
||||||
for s in (sc, sp):
|
for byte_access in (False,):
|
||||||
for key, val in test_strings:
|
sc, sp = common.init(unlock=True, flash_byte_access=byte_access)
|
||||||
s.set(key, val)
|
|
||||||
assert common.memory_equals(sc, sp)
|
|
||||||
|
|
||||||
for key, val in test_strings:
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
assert s.delete(key)
|
for key, val in test_strings:
|
||||||
|
s.set(key, val)
|
||||||
|
s.set(key, val)
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
|
for s in (sc, sp):
|
||||||
|
for key, val in test_strings:
|
||||||
|
s.set(key, val)
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
|
for key, val in test_strings:
|
||||||
|
for s in (sc, sp):
|
||||||
|
assert s.delete(key)
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
|
|
||||||
def test_set_similar():
|
def test_set_similar():
|
||||||
sc, sp = common.init(unlock=True)
|
for byte_access in (True, False):
|
||||||
for s in (sc, sp):
|
sc, sp = common.init(unlock=True, flash_byte_access=byte_access)
|
||||||
s.set(0xBEEF, b"Satoshi")
|
for s in (sc, sp):
|
||||||
s.set(0xBEEF, b"satoshi")
|
s.set(0xBEEF, b"Satoshi")
|
||||||
assert common.memory_equals(sc, sp)
|
s.set(0xBEEF, b"satoshi")
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
s.wipe()
|
s.wipe()
|
||||||
s.unlock("")
|
s.unlock("")
|
||||||
s.set(0xBEEF, b"satoshi")
|
s.set(0xBEEF, b"satoshi")
|
||||||
s.set(0xBEEF, b"Satoshi")
|
s.set(0xBEEF, b"Satoshi")
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
s.wipe()
|
s.wipe()
|
||||||
s.unlock("")
|
s.unlock("")
|
||||||
s.set(0xBEEF, b"satoshi")
|
s.set(0xBEEF, b"satoshi")
|
||||||
s.set(0xBEEF, b"Satoshi")
|
s.set(0xBEEF, b"Satoshi")
|
||||||
s.set(0xBEEF, b"Satoshi")
|
s.set(0xBEEF, b"Satoshi")
|
||||||
s.set(0xBEEF, b"SatosHi")
|
s.set(0xBEEF, b"SatosHi")
|
||||||
s.set(0xBEEF, b"satoshi")
|
s.set(0xBEEF, b"satoshi")
|
||||||
s.set(0xBEEF, b"satoshi\x00")
|
s.set(0xBEEF, b"satoshi\x00")
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
|
|
||||||
def test_set_locked():
|
def test_set_locked():
|
||||||
sc, sp = common.init()
|
for byte_access in (True, False):
|
||||||
for s in (sc, sp):
|
sc, sp = common.init(flash_byte_access=byte_access)
|
||||||
with pytest.raises(RuntimeError):
|
for s in (sc, sp):
|
||||||
s.set(0x0303, b"test")
|
with pytest.raises(RuntimeError):
|
||||||
with pytest.raises(RuntimeError):
|
s.set(0x0303, b"test")
|
||||||
s.set(0x8003, b"test")
|
with pytest.raises(RuntimeError):
|
||||||
assert common.memory_equals(sc, sp)
|
s.set(0x8003, b"test")
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
s.set(0xC001, b"Ahoj")
|
s.set(0xC001, b"Ahoj")
|
||||||
s.set(0xC003, b"test")
|
s.set(0xC003, b"test")
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
assert s.get(0xC001) == b"Ahoj"
|
assert s.get(0xC001) == b"Ahoj"
|
||||||
assert s.get(0xC003) == b"test"
|
assert s.get(0xC003) == b"test"
|
||||||
|
|
||||||
|
|
||||||
def test_counter():
|
def test_counter():
|
||||||
sc, sp = common.init(unlock=True)
|
for byte_access in (True, False):
|
||||||
for i in range(0, 200):
|
sc, sp = common.init(unlock=True, flash_byte_access=byte_access)
|
||||||
|
for i in range(0, 200):
|
||||||
|
for s in (sc, sp):
|
||||||
|
assert i == s.next_counter(0xC001)
|
||||||
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
assert i == s.next_counter(0xC001)
|
s.lock()
|
||||||
|
s.set_counter(0xC001, 500)
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for s in (sc, sp):
|
for i in range(501, 700):
|
||||||
s.lock()
|
for s in (sc, sp):
|
||||||
s.set_counter(0xC001, 500)
|
assert i == s.next_counter(0xC001)
|
||||||
assert common.memory_equals(sc, sp)
|
assert common.memory_equals(sc, sp)
|
||||||
|
|
||||||
for i in range(501, 700):
|
|
||||||
for s in (sc, sp):
|
for s in (sc, sp):
|
||||||
assert i == s.next_counter(0xC001)
|
with pytest.raises(RuntimeError):
|
||||||
assert common.memory_equals(sc, sp)
|
s.set_counter(0xC001, consts.UINT32_MAX + 1)
|
||||||
|
|
||||||
for s in (sc, sp):
|
start = consts.UINT32_MAX - 100
|
||||||
with pytest.raises(RuntimeError):
|
s.set_counter(0xC001, start)
|
||||||
s.set_counter(0xC001, consts.UINT32_MAX + 1)
|
for i in range(start, consts.UINT32_MAX):
|
||||||
|
assert i + 1 == s.next_counter(0xC001)
|
||||||
|
|
||||||
start = consts.UINT32_MAX - 100
|
with pytest.raises(RuntimeError):
|
||||||
s.set_counter(0xC001, start)
|
s.next_counter(0xC001)
|
||||||
for i in range(start, consts.UINT32_MAX):
|
|
||||||
assert i + 1 == s.next_counter(0xC001)
|
|
||||||
|
|
||||||
with pytest.raises(RuntimeError):
|
assert common.memory_equals(sc, sp)
|
||||||
s.next_counter(0xC001)
|
|
||||||
|
|
||||||
assert common.memory_equals(sc, sp)
|
|
||||||
|
@ -59,18 +59,19 @@ def test_upgrade():
|
|||||||
|
|
||||||
|
|
||||||
def test_python_set_sectors():
|
def test_python_set_sectors():
|
||||||
sp0 = StoragePy()
|
for byte_access in (True, False):
|
||||||
sp0.init(common.test_uid)
|
sp0 = StoragePy(byte_access)
|
||||||
assert sp0.unlock("")
|
sp0.init(common.test_uid)
|
||||||
set_values(sp0)
|
assert sp0.unlock("")
|
||||||
for _ in range(10):
|
set_values(sp0)
|
||||||
assert not sp0.unlock("3")
|
for _ in range(10):
|
||||||
assert sp0.get_pin_rem() == 6
|
assert not sp0.unlock("3")
|
||||||
|
assert sp0.get_pin_rem() == 6
|
||||||
|
|
||||||
sp1 = StoragePy()
|
sp1 = StoragePy(byte_access)
|
||||||
sp1.nc._set_sectors(sp0._dump())
|
sp1.nc._set_sectors(sp0._dump())
|
||||||
sp1.init(common.test_uid)
|
sp1.init(common.test_uid)
|
||||||
common.memory_equals(sp0, sp1)
|
common.memory_equals(sp0, sp1)
|
||||||
|
|
||||||
assert sp1.get_pin_rem() == 6
|
assert sp1.get_pin_rem() == 6
|
||||||
check_values(sp1)
|
check_values(sp1)
|
||||||
|
Loading…
Reference in New Issue
Block a user