robustness

(cherry picked from commit 85ff2dcf45)
This commit is contained in:
Peter D. Gray 2025-01-17 12:04:37 -05:00 committed by scgbckbone
parent a180fe4b20
commit b6d58a7468
2 changed files with 23 additions and 16 deletions

View File

@ -11,7 +11,6 @@ def make_flash_fs():
os.VfsLfs2.mkfs(fl)
os.mount(fl, '/flash')
os.mkdir('/flash/settings')
def make_psram_fs():

View File

@ -8,13 +8,13 @@
# - recover from empty/blank/failed chips w/o user action
#
# Result:
# - up to 4k of values supported (after json encoding)
# - encrypted and stored in SPI flash, in last 128k area
# - up to a few k of values supported (after json encoding)
# - encrypted and stored in main flash, in a dedicated 512k area
# - AES encryption key is derived from actual wallet secret
# - if logged out, then use fixed key instead (ie. it's public)
# - you cannot move data between slots because AES-CTR with CTR seed based on slot #
# - SHA-256 check on decrypted data
# - (Mk4) each slot is a file on /flash/settings
# - each "slot" is a file in /flash/settings; in Mk1-3 was SPI flash block
# - os.sync() not helpful because block device under filesystem doesnt implement it
#
import os, ujson, ustruct, ckcc, gc, ngu, aes256ctr, version
@ -177,6 +177,13 @@ class SettingsObject:
return (blocks-bfree) / blocks
def _open_file(self, pos, mode='rb'):
if 'w' in mode:
# make directory, when needed (recovery/robustness)
try:
os.stat(MK4_WORKDIR)
except OSError: # ENOENT
os.mkdir(MK4_WORKDIR[:-1])
return open(MK4_FILENAME(pos), mode)
def _slot_is_blank(self, pos, buf):
@ -193,13 +200,13 @@ class SettingsObject:
fn = MK4_FILENAME(pos)
try:
os.remove(fn)
except Exception:
# Error (ENOENT) expected here when saving first time, because the
except:
# OSError (ENOENT) expected here when saving first time, because the
# "old" slot was not in use
pass
def _read_slot(self, pos, decryptor):
# Mk4 is just reading a binary file and decrypt as we go.
# read a binary file and decrypt as we go.
with self._open_file(pos) as fd:
# missing ftell(), so emulate
ln = fd.seek(0, 2)
@ -244,9 +251,12 @@ class SettingsObject:
fd.write(aes(chk.digest()))
def _used_slots(self):
# mk4: faster list of slots in use; doesn't open them
files = os.listdir(MK4_WORKDIR)
return [int(fn[0:-4], 16) for fn in files if fn.endswith('.aes')]
# list of slots in use; doesn't open them
try:
files = os.listdir(MK4_WORKDIR)
return [int(fn[0:-4], 16) for fn in files if fn.endswith('.aes')]
except:
return []
def _nonempty_slots(self, dis=None):
# generate slots that are non-empty
@ -393,8 +403,9 @@ class SettingsObject:
set = put
def remove_key(self, kn):
self.current.pop(kn, None)
self.changed()
if kn in self.current:
self.current.pop(kn, None)
self.changed()
def merge_previous_active(self, previous):
import pyb
@ -452,11 +463,8 @@ class SettingsObject:
call_later_ms(250, self.write_out)
def find_spot(self, not_here=0):
# search for a blank sector to use
# - check randomly and pick first blank one (wear leveling, deniability)
# - we will write and then erase old slot
# search for a blank slot to use
# - if "full", blow away a random one
# on mk4, use the filesystem to see what's already taken
avail = set(SLOTS) - set(self._used_slots())
avail.discard(not_here)