2019-04-25 14:38:56 +00:00
|
|
|
from . import consts, helpers, prng
|
|
|
|
|
2024-02-05 11:52:08 +00:00
|
|
|
# Length of items in the PIN entry log
|
|
|
|
PIN_LOG_GUARD_KEY_SIZE = 4
|
2019-04-25 14:38:56 +00:00
|
|
|
|
2024-02-05 11:52:08 +00:00
|
|
|
# Values used for the guard key integrity check.
|
|
|
|
GUARD_KEY_MODULUS = 6311
|
|
|
|
GUARD_KEY_REMAINDER = 15
|
|
|
|
GUARD_KEY_RANDOM_MAX = (0xFFFFFFFF // GUARD_KEY_MODULUS) + 1
|
|
|
|
|
|
|
|
# Length of both success log and entry log
|
|
|
|
PIN_LOG_SIZE = 64
|
|
|
|
|
|
|
|
# Used for in guard bits operations.
|
|
|
|
LOW_MASK = 0x55555555
|
|
|
|
|
|
|
|
# Log initialized to all FFs.
|
|
|
|
ALL_FF_LOG = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
|
|
|
|
|
|
|
|
|
|
|
def expand_to_log_size(value: int) -> int:
|
|
|
|
result = 0
|
|
|
|
for i in range(0, PIN_LOG_SIZE, 4):
|
|
|
|
result = result | (value << i * 8)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
class PinLogBitwise:
|
2019-04-25 14:38:56 +00:00
|
|
|
def __init__(self, norcow):
|
|
|
|
self.norcow = norcow
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
guard_key = self._generate_guard_key()
|
|
|
|
guard_mask, guard = self.derive_guard_mask_and_value(guard_key)
|
|
|
|
|
2024-02-05 11:52:08 +00:00
|
|
|
pin_success_log = (~guard_mask & ALL_FF_LOG) | guard
|
|
|
|
pin_entry_log = (~guard_mask & ALL_FF_LOG) | guard
|
2019-04-25 14:38:56 +00:00
|
|
|
self._write_log(guard_key, pin_success_log, pin_entry_log)
|
|
|
|
|
|
|
|
def derive_guard_mask_and_value(self, guard_key: int) -> (int, int):
|
|
|
|
if guard_key > 0xFFFFFFFF:
|
|
|
|
raise ValueError("Invalid guard key")
|
|
|
|
|
2024-02-05 11:52:08 +00:00
|
|
|
guard_mask = ((guard_key & LOW_MASK) << 1) | (
|
|
|
|
(~guard_key & 0xFFFFFFFF) & LOW_MASK
|
2019-04-25 14:38:56 +00:00
|
|
|
)
|
2024-02-05 11:52:08 +00:00
|
|
|
guard = (((guard_key & LOW_MASK) << 1) & guard_key) | (
|
|
|
|
((~guard_key & 0xFFFFFFFF) & LOW_MASK) & (guard_key >> 1)
|
2019-04-25 14:38:56 +00:00
|
|
|
)
|
2024-02-05 11:52:08 +00:00
|
|
|
return expand_to_log_size(guard_mask), expand_to_log_size(guard)
|
2019-04-25 14:38:56 +00:00
|
|
|
|
|
|
|
def write_attempt(self):
|
|
|
|
guard_key, pin_success_log, pin_entry_log = self._get_logs()
|
|
|
|
guard_mask, guard = self.derive_guard_mask_and_value(guard_key)
|
|
|
|
assert (pin_entry_log & guard_mask) == guard
|
|
|
|
|
|
|
|
clean_pin_entry_log = self.remove_guard_bits(guard_mask, pin_entry_log)
|
|
|
|
clean_pin_entry_log = clean_pin_entry_log >> 2 # set 11 to 00
|
2024-02-05 11:52:08 +00:00
|
|
|
pin_entry_log = (clean_pin_entry_log & (~guard_mask & ALL_FF_LOG)) | guard
|
2019-04-25 14:38:56 +00:00
|
|
|
|
|
|
|
self._write_log(guard_key, pin_success_log, pin_entry_log)
|
|
|
|
|
|
|
|
def write_success(self):
|
|
|
|
guard_key, pin_success_log, pin_entry_log = self._get_logs()
|
|
|
|
pin_success_log = pin_entry_log
|
|
|
|
|
|
|
|
self._write_log(guard_key, pin_success_log, pin_entry_log)
|
|
|
|
|
|
|
|
def get_failures_count(self) -> int:
|
|
|
|
guard_key, pin_succes_log, pin_entry_log = self._get_logs()
|
|
|
|
guard_mask, _ = self.derive_guard_mask_and_value(guard_key)
|
|
|
|
|
|
|
|
pin_succes_log = self.remove_guard_bits(guard_mask, pin_succes_log)
|
|
|
|
pin_entry_log = self.remove_guard_bits(guard_mask, pin_entry_log)
|
|
|
|
|
|
|
|
# divide by two because bits are doubled after remove_guard_bits()
|
|
|
|
return bin(pin_succes_log - pin_entry_log).count("1") // 2
|
|
|
|
|
|
|
|
def remove_guard_bits(self, guard_mask: int, log: int) -> int:
|
|
|
|
"""
|
|
|
|
Removes all guard bits and replaces each guard bit
|
|
|
|
with its neighbour value.
|
|
|
|
Example: 0g0gg1 -> 000011
|
|
|
|
"""
|
2024-02-05 11:52:08 +00:00
|
|
|
log = log & (~guard_mask & ALL_FF_LOG)
|
|
|
|
log = ((log >> 1) | log) & expand_to_log_size(LOW_MASK)
|
2019-04-25 14:38:56 +00:00
|
|
|
log = log | (log << 1)
|
|
|
|
return log
|
|
|
|
|
|
|
|
def _generate_guard_key(self) -> int:
|
|
|
|
while True:
|
2024-02-05 11:52:08 +00:00
|
|
|
r = prng.random_uniform(GUARD_KEY_RANDOM_MAX)
|
|
|
|
r = (r * GUARD_KEY_MODULUS + GUARD_KEY_REMAINDER) & 0xFFFFFFFF
|
2019-04-25 14:38:56 +00:00
|
|
|
if self._check_guard_key(r):
|
|
|
|
return r
|
|
|
|
|
|
|
|
def _check_guard_key(self, guard_key: int) -> bool:
|
|
|
|
"""
|
|
|
|
Checks if guard_key is congruent to 15 modulo 6311 and
|
|
|
|
some other conditions, see the docs.
|
|
|
|
"""
|
|
|
|
count = (guard_key & 0x22222222) + ((guard_key >> 2) & 0x22222222)
|
|
|
|
count = count + (count >> 4)
|
|
|
|
|
|
|
|
zero_runs = ~guard_key & 0xFFFFFFFF
|
|
|
|
zero_runs = zero_runs & (zero_runs >> 2)
|
|
|
|
zero_runs = zero_runs & (zero_runs >> 1)
|
|
|
|
zero_runs = zero_runs & (zero_runs >> 1)
|
|
|
|
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)
|
|
|
|
|
|
|
|
return (
|
|
|
|
((count & 0x0E0E0E0E) == 0x04040404)
|
|
|
|
& (one_runs == 0)
|
|
|
|
& (zero_runs == 0)
|
2024-02-05 11:52:08 +00:00
|
|
|
& (guard_key % GUARD_KEY_MODULUS == GUARD_KEY_REMAINDER)
|
2019-04-25 14:38:56 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
def _get_logs(self) -> (int, int, int):
|
|
|
|
pin_log = self.norcow.get(consts.PIN_LOG_KEY)
|
2024-02-05 11:52:08 +00:00
|
|
|
guard_key = pin_log[:PIN_LOG_GUARD_KEY_SIZE]
|
2019-04-25 14:38:56 +00:00
|
|
|
guard_key = helpers.word_to_int(guard_key)
|
|
|
|
guard_mask, guard = self.derive_guard_mask_and_value(guard_key)
|
2024-02-05 11:52:08 +00:00
|
|
|
pin_entry_log = pin_log[PIN_LOG_GUARD_KEY_SIZE + PIN_LOG_SIZE :]
|
2019-04-25 14:38:56 +00:00
|
|
|
pin_entry_log = helpers.to_int_by_words(pin_entry_log)
|
|
|
|
pin_success_log = pin_log[
|
2024-02-05 11:52:08 +00:00
|
|
|
PIN_LOG_GUARD_KEY_SIZE : PIN_LOG_GUARD_KEY_SIZE + PIN_LOG_SIZE
|
2019-04-25 14:38:56 +00:00
|
|
|
]
|
|
|
|
pin_success_log = helpers.to_int_by_words(pin_success_log)
|
|
|
|
|
|
|
|
return guard_key, pin_success_log, pin_entry_log
|
|
|
|
|
|
|
|
def _write_log(self, guard_key: int, pin_success_log: int, pin_entry_log: int):
|
|
|
|
pin_log = (
|
|
|
|
helpers.int_to_word(guard_key)
|
2024-02-05 11:52:08 +00:00
|
|
|
+ helpers.to_bytes_by_words(pin_success_log, PIN_LOG_SIZE)
|
|
|
|
+ helpers.to_bytes_by_words(pin_entry_log, PIN_LOG_SIZE)
|
2019-04-25 14:38:56 +00:00
|
|
|
)
|
2023-08-21 08:56:18 +00:00
|
|
|
if self.norcow.is_byte_access():
|
|
|
|
try:
|
|
|
|
self.norcow.replace(consts.PIN_LOG_KEY, pin_log)
|
|
|
|
except RuntimeError:
|
|
|
|
self.norcow.set(consts.PIN_LOG_KEY, pin_log)
|
|
|
|
else:
|
2019-04-25 14:38:56 +00:00
|
|
|
self.norcow.set(consts.PIN_LOG_KEY, pin_log)
|