[no changelog]pull/3522/head
parent
919d752598
commit
434ed04b7f
@ -0,0 +1,267 @@
|
||||
|
||||
// Values used in the guard key integrity check.
|
||||
#define GUARD_KEY_MODULUS 6311
|
||||
#define GUARD_KEY_REMAINDER 15
|
||||
|
||||
#define LOW_MASK 0x55555555
|
||||
|
||||
// The length of the guard key in words.
|
||||
#define GUARD_KEY_WORDS 1
|
||||
|
||||
// The length of the PIN entry log or the PIN success log in words.
|
||||
#define PIN_LOG_WORDS 16
|
||||
|
||||
// The length of a word in bytes.
|
||||
#define WORD_SIZE (sizeof(uint32_t))
|
||||
|
||||
static secbool check_guard_key(const uint32_t guard_key) {
|
||||
if (guard_key % GUARD_KEY_MODULUS != GUARD_KEY_REMAINDER) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
// Check that each byte of (guard_key & 0xAAAAAAAA) has exactly two bits set.
|
||||
uint32_t count = (guard_key & 0x22222222) + ((guard_key >> 2) & 0x22222222);
|
||||
count = count + (count >> 4);
|
||||
if ((count & 0x0e0e0e0e) != 0x04040404) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
// Check that the guard_key does not contain a run of 5 (or more) zeros or
|
||||
// ones.
|
||||
uint32_t zero_runs = ~guard_key;
|
||||
zero_runs = zero_runs & (zero_runs >> 2);
|
||||
zero_runs = zero_runs & (zero_runs >> 1);
|
||||
zero_runs = zero_runs & (zero_runs >> 1);
|
||||
|
||||
uint32_t one_runs = guard_key;
|
||||
one_runs = one_runs & (one_runs >> 2);
|
||||
one_runs = one_runs & (one_runs >> 1);
|
||||
one_runs = one_runs & (one_runs >> 1);
|
||||
|
||||
if ((one_runs != 0) || (zero_runs != 0)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
static uint32_t generate_guard_key(void) {
|
||||
uint32_t guard_key = 0;
|
||||
do {
|
||||
guard_key = random_uniform((UINT32_MAX / GUARD_KEY_MODULUS) + 1) *
|
||||
GUARD_KEY_MODULUS +
|
||||
GUARD_KEY_REMAINDER;
|
||||
} while (sectrue != check_guard_key(guard_key));
|
||||
return guard_key;
|
||||
}
|
||||
|
||||
static secbool expand_guard_key(const uint32_t guard_key, uint32_t *guard_mask,
|
||||
uint32_t *guard) {
|
||||
if (sectrue != check_guard_key(guard_key)) {
|
||||
handle_fault("guard key check");
|
||||
return secfalse;
|
||||
}
|
||||
*guard_mask = ((guard_key & LOW_MASK) << 1) | ((~guard_key) & LOW_MASK);
|
||||
*guard = (((guard_key & LOW_MASK) << 1) & guard_key) |
|
||||
(((~guard_key) & LOW_MASK) & (guard_key >> 1));
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
static secbool pin_logs_init(uint32_t fails) {
|
||||
if (fails >= PIN_MAX_TRIES) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
// The format of the PIN_LOGS_KEY entry is:
|
||||
// guard_key (1 word), pin_success_log (PIN_LOG_WORDS), pin_entry_log
|
||||
// (PIN_LOG_WORDS)
|
||||
uint32_t logs[GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS] = {0};
|
||||
|
||||
logs[0] = generate_guard_key();
|
||||
|
||||
uint32_t guard_mask = 0;
|
||||
uint32_t guard = 0;
|
||||
wait_random();
|
||||
if (sectrue != expand_guard_key(logs[0], &guard_mask, &guard)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
uint32_t unused = guard | ~guard_mask;
|
||||
for (size_t i = 0; i < 2 * PIN_LOG_WORDS; ++i) {
|
||||
logs[GUARD_KEY_WORDS + i] = unused;
|
||||
}
|
||||
|
||||
// Set the first word of the PIN entry log to indicate the requested number of
|
||||
// fails.
|
||||
logs[GUARD_KEY_WORDS + PIN_LOG_WORDS] =
|
||||
((((uint32_t)0xFFFFFFFF) >> (2 * fails)) & ~guard_mask) | guard;
|
||||
|
||||
return norcow_set(PIN_LOGS_KEY, logs, sizeof(logs));
|
||||
}
|
||||
|
||||
static secbool pin_fails_reset(void) {
|
||||
const void *logs = NULL;
|
||||
uint16_t len = 0;
|
||||
|
||||
if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) ||
|
||||
len != WORD_SIZE * (GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
uint32_t new_logs[GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS];
|
||||
secbool edited = secfalse;
|
||||
memcpy(new_logs, logs, len);
|
||||
|
||||
uint32_t guard_mask = 0;
|
||||
uint32_t guard = 0;
|
||||
wait_random();
|
||||
if (sectrue !=
|
||||
expand_guard_key(*(const uint32_t *)logs, &guard_mask, &guard)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
uint32_t unused = guard | ~guard_mask;
|
||||
const uint32_t *success_log = ((const uint32_t *)logs) + GUARD_KEY_WORDS;
|
||||
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) {
|
||||
if (edited == sectrue) {
|
||||
return norcow_set(PIN_LOGS_KEY, new_logs, sizeof(new_logs));
|
||||
}
|
||||
return sectrue;
|
||||
}
|
||||
if (success_log[i] != guard) {
|
||||
if (new_logs[(i + GUARD_KEY_WORDS)] != entry_log[i]) {
|
||||
edited = sectrue;
|
||||
new_logs[(i + GUARD_KEY_WORDS)] = entry_log[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return pin_logs_init(0);
|
||||
}
|
||||
|
||||
secbool pin_fails_increase(void) {
|
||||
const void *logs = NULL;
|
||||
uint16_t len = 0;
|
||||
|
||||
wait_random();
|
||||
if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) ||
|
||||
len != WORD_SIZE * (GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS)) {
|
||||
handle_fault("no PIN logs");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
uint32_t new_logs[GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS];
|
||||
memcpy(new_logs, logs, len);
|
||||
|
||||
uint32_t guard_mask = 0;
|
||||
uint32_t guard = 0;
|
||||
wait_random();
|
||||
if (sectrue !=
|
||||
expand_guard_key(*(const uint32_t *)logs, &guard_mask, &guard)) {
|
||||
handle_fault("guard key expansion");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
const uint32_t *entry_log =
|
||||
((const uint32_t *)logs) + GUARD_KEY_WORDS + PIN_LOG_WORDS;
|
||||
for (size_t i = 0; i < PIN_LOG_WORDS; ++i) {
|
||||
wait_random();
|
||||
if ((entry_log[i] & guard_mask) != guard) {
|
||||
handle_fault("guard bits check");
|
||||
return secfalse;
|
||||
}
|
||||
if (entry_log[i] != guard) {
|
||||
wait_random();
|
||||
uint32_t word = entry_log[i] & ~guard_mask;
|
||||
word = ((word >> 1) | word) & LOW_MASK;
|
||||
word = (word >> 2) | (word >> 1);
|
||||
|
||||
wait_random();
|
||||
|
||||
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;
|
||||
}
|
||||
return sectrue;
|
||||
}
|
||||
}
|
||||
handle_fault("PIN log exhausted");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
static secbool pin_get_fails(uint32_t *ctr) {
|
||||
*ctr = PIN_MAX_TRIES;
|
||||
|
||||
const void *logs = NULL;
|
||||
uint16_t len = 0;
|
||||
wait_random();
|
||||
if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) ||
|
||||
len != WORD_SIZE * (GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS)) {
|
||||
handle_fault("no PIN logs");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
uint32_t guard_mask = 0;
|
||||
uint32_t guard = 0;
|
||||
wait_random();
|
||||
if (sectrue !=
|
||||
expand_guard_key(*(const uint32_t *)logs, &guard_mask, &guard)) {
|
||||
handle_fault("guard key expansion");
|
||||
return secfalse;
|
||||
}
|
||||
const uint32_t unused = guard | ~guard_mask;
|
||||
|
||||
const uint32_t *success_log = ((const uint32_t *)logs) + GUARD_KEY_WORDS;
|
||||
const uint32_t *entry_log = success_log + PIN_LOG_WORDS;
|
||||
volatile int current = -1;
|
||||
volatile size_t i = 0;
|
||||
for (i = 0; i < PIN_LOG_WORDS; ++i) {
|
||||
if ((entry_log[i] & guard_mask) != guard ||
|
||||
(success_log[i] & guard_mask) != guard ||
|
||||
(entry_log[i] & success_log[i]) != entry_log[i]) {
|
||||
handle_fault("PIN logs format check");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
if (current == -1) {
|
||||
if (entry_log[i] != guard) {
|
||||
current = i;
|
||||
}
|
||||
} else {
|
||||
if (entry_log[i] != unused) {
|
||||
handle_fault("PIN entry log format check");
|
||||
return secfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current < 0 || current >= PIN_LOG_WORDS || i != PIN_LOG_WORDS) {
|
||||
handle_fault("PIN log exhausted");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
// Strip the guard bits from the current entry word and duplicate each data
|
||||
// bit.
|
||||
wait_random();
|
||||
uint32_t word = entry_log[current] & ~guard_mask;
|
||||
word = ((word >> 1) | word) & LOW_MASK;
|
||||
word = word | (word << 1);
|
||||
// Verify that the entry word has form 0*1*.
|
||||
if ((word & (word + 1)) != 0) {
|
||||
handle_fault("PIN entry log format check");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
if (current == 0) {
|
||||
++current;
|
||||
}
|
||||
|
||||
// Count the number of set bits in the two current words of the success log.
|
||||
wait_random();
|
||||
*ctr = hamming_weight(success_log[current - 1] ^ entry_log[current - 1]) +
|
||||
hamming_weight(success_log[current] ^ entry_log[current]);
|
||||
return sectrue;
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
|
||||
#if FLASH_BLOCK_WORDS <= 1
|
||||
#error "FLASH_BLOCK_WORDS must be at least 2 to fit the counter and header"
|
||||
#endif
|
||||
|
||||
#define PIN_LOG_HALFWORDS (((FLASH_BLOCK_WORDS - 1) * sizeof(uint32_t)) / 2)
|
||||
|
||||
static uint16_t expand_counter(uint16_t c) {
|
||||
c = ((c << 4) | c) & 0x0f0f;
|
||||
c = ((c << 2) | c) & 0x3333;
|
||||
c = ((c << 1) | c) & 0x5555;
|
||||
c = ((c << 1) | c) ^ 0xaaaa;
|
||||
return c;
|
||||
}
|
||||
|
||||
static uint16_t compress_counter(uint16_t c) {
|
||||
if (((c ^ (c << 1)) & 0xAAAA) != 0xAAAA) {
|
||||
handle_fault("ill-formed counter");
|
||||
}
|
||||
c = c & 0x5555;
|
||||
c = ((c >> 1) | c) & 0x3333;
|
||||
c = ((c >> 2) | c) & 0x0f0f;
|
||||
c = ((c >> 4) | c) & 0x00ff;
|
||||
return c;
|
||||
}
|
||||
|
||||
static secbool pin_get_fails(uint32_t *ctr) {
|
||||
const void *logs = NULL;
|
||||
uint16_t len = 0;
|
||||
|
||||
wait_random();
|
||||
|
||||
if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) ||
|
||||
len != PIN_LOG_HALFWORDS * sizeof(uint16_t)) {
|
||||
handle_fault("no PIN logs");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
uint16_t c = compress_counter(((uint16_t *)logs)[0]);
|
||||
|
||||
uint16_t correct_bytes_cnt = 0;
|
||||
|
||||
for (uint8_t i = 0; i < PIN_LOG_HALFWORDS; i++) {
|
||||
wait_random();
|
||||
correct_bytes_cnt += compress_counter(((uint16_t *)logs)[i]) == c;
|
||||
*ctr = c;
|
||||
}
|
||||
|
||||
if (correct_bytes_cnt != PIN_LOG_HALFWORDS) {
|
||||
handle_fault("PIN logs corrupted");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
return sectrue * (correct_bytes_cnt == PIN_LOG_HALFWORDS);
|
||||
}
|
||||
|
||||
static secbool pin_logs_init(uint32_t fails) {
|
||||
wait_random();
|
||||
|
||||
uint16_t logs[PIN_LOG_HALFWORDS];
|
||||
uint16_t ctr = expand_counter(fails);
|
||||
|
||||
for (uint8_t i = 0; i < PIN_LOG_HALFWORDS; i++) {
|
||||
logs[i] = ctr;
|
||||
}
|
||||
|
||||
if (fails != compress_counter(ctr)) {
|
||||
handle_fault("PIN logs increase failed");
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
return norcow_set(PIN_LOGS_KEY, logs, sizeof(logs));
|
||||
}
|
||||
|
||||
static secbool pin_fails_reset(void) { return pin_logs_init(0); }
|
||||
|
||||
secbool pin_fails_increase(void) {
|
||||
uint32_t fails;
|
||||
|
||||
if (sectrue != pin_get_fails(&fails)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
fails++;
|
||||
|
||||
return pin_logs_init(fails);
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
from . import consts
|
||||
|
||||
PIN_LOG_HALFWORDS = int(3 * (consts.WORD_SIZE / 2))
|
||||
|
||||
|
||||
def expand_counter(c: int) -> int:
|
||||
c = ((c << 4) | c) & 0x0F0F
|
||||
c = ((c << 2) | c) & 0x3333
|
||||
c = ((c << 1) | c) & 0x5555
|
||||
c = ((c << 1) | c) ^ 0xAAAA
|
||||
return c
|
||||
|
||||
|
||||
def compress_counter(c: int) -> int:
|
||||
c = c & 0x5555
|
||||
c = ((c >> 1) | c) & 0x3333
|
||||
c = ((c >> 2) | c) & 0x0F0F
|
||||
c = ((c >> 4) | c) & 0x00FF
|
||||
return c
|
||||
|
||||
|
||||
class PinLogBlockwise:
|
||||
def __init__(self, norcow):
|
||||
self.norcow = norcow
|
||||
|
||||
def init(self):
|
||||
self._write_log(0)
|
||||
|
||||
def write_attempt(self):
|
||||
self._write_log(self.get_failures_count() + 1)
|
||||
|
||||
def write_success(self):
|
||||
self._write_log(0)
|
||||
|
||||
def get_failures_count(self) -> int:
|
||||
return self._get_logs()
|
||||
|
||||
def _get_logs(self) -> int:
|
||||
logs = self.norcow.get(consts.PIN_LOG_KEY)
|
||||
|
||||
if logs is None:
|
||||
raise ValueError("No PIN logs")
|
||||
|
||||
ctr = int.from_bytes(logs[:2], "little")
|
||||
|
||||
fails = compress_counter(ctr)
|
||||
|
||||
for i in range(2, PIN_LOG_HALFWORDS, 2):
|
||||
if fails != compress_counter(int.from_bytes(logs[i : i + 2], "little")):
|
||||
raise ValueError("PIN logs corrupted")
|
||||
|
||||
return fails
|
||||
|
||||
def _write_log(self, fails: int):
|
||||
ctr = expand_counter(fails)
|
||||
data = ctr.to_bytes(2, "little")
|
||||
for _ in range(1, PIN_LOG_HALFWORDS):
|
||||
data += ctr.to_bytes(2, "little")
|
||||
self.norcow.set(consts.PIN_LOG_KEY, data)
|
@ -1,13 +1,14 @@
|
||||
from ..src import consts, helpers
|
||||
from ..src import helpers
|
||||
from ..src.pin_log_bitwise import PIN_LOG_SIZE
|
||||
|
||||
|
||||
def test_read_bytes_by_words():
|
||||
array = b"\x04\x03\x02\x01\x08\x07\x06\x05"
|
||||
n = helpers.to_int_by_words(array)
|
||||
assert n == 0x0102030405060708
|
||||
assert array == helpers.to_bytes_by_words(n, consts.PIN_LOG_SIZE)[56:]
|
||||
assert array == helpers.to_bytes_by_words(n, PIN_LOG_SIZE)[56:]
|
||||
|
||||
array = b"\xFF\xFF\xFF\x01\x01\x05\x09\x01"
|
||||
n = helpers.to_int_by_words(array)
|
||||
assert n == 0x01FFFFFF01090501
|
||||
assert array == helpers.to_bytes_by_words(n, consts.PIN_LOG_SIZE)[56:]
|
||||
assert array == helpers.to_bytes_by_words(n, PIN_LOG_SIZE)[56:]
|
||||
|
Loading…
Reference in new issue