Merge pull request #634 from trezor/andrewkozlik/sd-protect-ui

SD protect: Allow user to retry/abort on write failure
pull/635/head
Pavol Rusnak 5 years ago committed by GitHub
commit e485ac50db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,7 +8,7 @@ if __debug__:
from apps.debug import confirm_signal from apps.debug import confirm_signal
if False: if False:
from typing import Any, Callable from typing import Any, Callable, Optional
from trezor import ui from trezor import ui
from trezor.ui.confirm import ButtonContent, ButtonStyleType from trezor.ui.confirm import ButtonContent, ButtonStyleType
from trezor.ui.loader import LoaderStyleType from trezor.ui.loader import LoaderStyleType
@ -18,9 +18,9 @@ async def confirm(
ctx: wire.Context, ctx: wire.Context,
content: ui.Component, content: ui.Component,
code: int = ButtonRequestType.Other, code: int = ButtonRequestType.Other,
confirm: ButtonContent = Confirm.DEFAULT_CONFIRM, confirm: Optional[ButtonContent] = Confirm.DEFAULT_CONFIRM,
confirm_style: ButtonStyleType = Confirm.DEFAULT_CONFIRM_STYLE, confirm_style: ButtonStyleType = Confirm.DEFAULT_CONFIRM_STYLE,
cancel: ButtonContent = Confirm.DEFAULT_CANCEL, cancel: Optional[ButtonContent] = Confirm.DEFAULT_CANCEL,
cancel_style: ButtonStyleType = Confirm.DEFAULT_CANCEL_STYLE, cancel_style: ButtonStyleType = Confirm.DEFAULT_CANCEL_STYLE,
major_confirm: bool = False, major_confirm: bool = False,
) -> bool: ) -> bool:

@ -30,7 +30,7 @@ async def _wrong_card_dialog(ctx: Optional[wire.Context]) -> None:
text.br_half() text.br_half()
if SD_CARD_HOT_SWAPPABLE: if SD_CARD_HOT_SWAPPABLE:
text.normal("Please insert the", "correct SD card for", "this device.") text.normal("Please insert the", "correct SD card for", "this device.")
btn_confirm = "Retry" btn_confirm = "Retry" # type: Optional[str]
btn_cancel = "Abort" btn_cancel = "Abort"
else: else:
text.normal("Please unplug the", "device and insert the", "correct SD card.") text.normal("Please unplug the", "device and insert the", "correct SD card.")
@ -51,7 +51,7 @@ async def _insert_card_dialog(ctx: Optional[wire.Context]) -> None:
text.br_half() text.br_half()
if SD_CARD_HOT_SWAPPABLE: if SD_CARD_HOT_SWAPPABLE:
text.normal("Please insert your", "SD card.") text.normal("Please insert your", "SD card.")
btn_confirm = "Retry" btn_confirm = "Retry" # type: Optional[str]
btn_cancel = "Abort" btn_cancel = "Abort"
else: else:
text.normal("Please unplug the", "device and insert your", "SD card.") text.normal("Please unplug the", "device and insert your", "SD card.")
@ -88,6 +88,27 @@ def _get_salt_path(new: bool = False) -> str:
return "%s/salt" % _get_device_dir() return "%s/salt" % _get_device_dir()
def _load_salt(fs: io.FatFS, auth_key: bytes, path: str) -> Optional[bytearray]:
# Load the salt file if it exists.
try:
with fs.open(path, "r") as f:
salt = bytearray(SD_SALT_LEN_BYTES)
stored_tag = bytearray(SD_SALT_AUTH_TAG_LEN_BYTES)
f.read(salt)
f.read(stored_tag)
except OSError:
return None
# Check the salt's authentication tag.
computed_tag = hmac.new(auth_key, salt, sha256).digest()[
:SD_SALT_AUTH_TAG_LEN_BYTES
]
if not consteq(computed_tag, stored_tag):
return None
return salt
async def request_sd_salt( async def request_sd_salt(
ctx: Optional[wire.Context], salt_auth_key: bytes ctx: Optional[wire.Context], salt_auth_key: bytes
) -> bytearray: ) -> bytearray:
@ -102,56 +123,34 @@ async def request_sd_salt(
try: try:
fs.mount() fs.mount()
salt = _load_salt(fs, salt_auth_key, salt_path)
# Load salt if it exists. if salt is not None:
salt = None # type: Optional[bytearray]
try:
with fs.open(salt_path, "r") as f:
salt = bytearray(SD_SALT_LEN_BYTES)
salt_tag = bytearray(SD_SALT_AUTH_TAG_LEN_BYTES)
f.read(salt)
f.read(salt_tag)
except OSError:
salt = None
if salt is not None and consteq(
hmac.new(salt_auth_key, salt, sha256).digest()[
:SD_SALT_AUTH_TAG_LEN_BYTES
],
salt_tag,
):
return salt return salt
# Load salt.new if it exists. # Check if there is a new salt.
new_salt = None # type: Optional[bytearray] salt = _load_salt(fs, salt_auth_key, new_salt_path)
try: if salt is not None:
with fs.open(new_salt_path, "r") as f:
new_salt = bytearray(SD_SALT_LEN_BYTES)
new_salt_tag = bytearray(SD_SALT_AUTH_TAG_LEN_BYTES)
f.read(new_salt)
f.read(new_salt_tag)
except OSError:
new_salt = None
if new_salt is not None and consteq(
hmac.new(salt_auth_key, new_salt, sha256).digest()[
:SD_SALT_AUTH_TAG_LEN_BYTES
],
new_salt_tag,
):
# SD salt regeneration was interrupted earlier. Bring into consistent state. # SD salt regeneration was interrupted earlier. Bring into consistent state.
# TODO Possibly overwrite salt file with random data. # TODO Possibly overwrite salt file with random data.
try: try:
fs.unlink(salt_path) fs.unlink(salt_path)
except OSError: except OSError:
pass pass
fs.rename(new_salt_path, salt_path)
return new_salt try:
fs.rename(new_salt_path, salt_path)
except OSError:
error_dialog = _write_failed_dialog(ctx)
else:
return salt
else:
# No valid salt file on this SD card.
error_dialog = _wrong_card_dialog(ctx)
finally: finally:
fs.unmount() fs.unmount()
sd.power(False) sd.power(False)
await _wrong_card_dialog(ctx) await error_dialog
async def set_sd_salt( async def set_sd_salt(
@ -195,7 +194,7 @@ async def commit_sd_salt(ctx: Optional[wire.Context]) -> None:
sd = io.SDCard() sd = io.SDCard()
fs = io.FatFS() fs = io.FatFS()
if not sd.power(True): if not sd.power(True):
raise IOError raise OSError
try: try:
fs.mount() fs.mount()
@ -216,7 +215,7 @@ async def remove_sd_salt(ctx: Optional[wire.Context]) -> None:
sd = io.SDCard() sd = io.SDCard()
fs = io.FatFS() fs = io.FatFS()
if not sd.power(True): if not sd.power(True):
raise IOError raise OSError
try: try:
fs.mount() fs.mount()

Loading…
Cancel
Save