mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-28 17:18:29 +00:00
core: fix boot loop after uploading invalid homescreen (#1205)
This commit is contained in:
parent
68e119c2c6
commit
1b982659c4
@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
### Fixed
|
||||
- CRW addresses are properly generated. [#1139]
|
||||
- Fix boot loop after uploading invalid homescreen. [#1118]
|
||||
|
||||
### Security
|
||||
|
||||
@ -265,6 +266,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
[#1089]: https://github.com/trezor/trezor-firmware/issues/1089
|
||||
[#1098]: https://github.com/trezor/trezor-firmware/issues/1098
|
||||
[#1115]: https://github.com/trezor/trezor-firmware/issues/1115
|
||||
[#1118]: https://github.com/trezor/trezor-firmware/issues/1118
|
||||
[#1126]: https://github.com/trezor/trezor-firmware/issues/1126
|
||||
[#1139]: https://github.com/trezor/trezor-firmware/issues/1139
|
||||
[#1159]: https://github.com/trezor/trezor-firmware/issues/1159
|
||||
|
@ -349,6 +349,37 @@ void display_icon(int x, int y, int w, int h, const void *data,
|
||||
}
|
||||
}
|
||||
|
||||
// see docs/misc/toif.md for defintion of the TOIF format
|
||||
bool display_toif_info(const uint8_t *data, uint32_t len, uint16_t *out_w,
|
||||
uint16_t *out_h, bool *out_grayscale) {
|
||||
if (len < 12 || memcmp(data, "TOI", 3) != 0) {
|
||||
return false;
|
||||
}
|
||||
bool grayscale = false;
|
||||
if (data[3] == 'f') {
|
||||
grayscale = false;
|
||||
} else if (data[3] == 'g') {
|
||||
grayscale = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t w = *(uint16_t *)(data + 4);
|
||||
uint16_t h = *(uint16_t *)(data + 6);
|
||||
|
||||
uint32_t datalen = *(uint32_t *)(data + 8);
|
||||
if (datalen != len - 12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out_w != NULL && out_h != NULL && out_grayscale != NULL) {
|
||||
*out_w = w;
|
||||
*out_h = h;
|
||||
*out_grayscale = grayscale;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if TREZOR_MODEL == T
|
||||
#include "loader.h"
|
||||
#endif
|
||||
|
@ -77,6 +77,8 @@ void display_bar(int x, int y, int w, int h, uint16_t c);
|
||||
void display_bar_radius(int x, int y, int w, int h, uint16_t c, uint16_t b,
|
||||
uint8_t r);
|
||||
|
||||
bool display_toif_info(const uint8_t *buf, uint32_t len, uint16_t *out_w,
|
||||
uint16_t *out_h, bool *out_grayscale);
|
||||
void display_image(int x, int y, int w, int h, const void *data,
|
||||
uint32_t datalen);
|
||||
void display_avatar(int x, int y, const void *data, uint32_t datalen,
|
||||
|
@ -118,6 +118,33 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_bar_radius_obj,
|
||||
8, 8,
|
||||
mod_trezorui_Display_bar_radius);
|
||||
|
||||
/// def toif_info(self, image: bytes) -> Tuple[int, int, bool]:
|
||||
/// """
|
||||
/// Returns tuple containing TOIF image dimensions: width, height, and
|
||||
/// whether it is grayscale.
|
||||
/// Raises an exception for corrupted images.
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorui_Display_toif_info(mp_obj_t self, mp_obj_t image) {
|
||||
mp_buffer_info_t buffer = {0};
|
||||
mp_get_buffer_raise(image, &buffer, MP_BUFFER_READ);
|
||||
|
||||
uint16_t w = 0;
|
||||
uint16_t h = 0;
|
||||
bool grayscale = false;
|
||||
bool valid = display_toif_info(buffer.buf, buffer.len, &w, &h, &grayscale);
|
||||
|
||||
if (!valid) {
|
||||
mp_raise_ValueError("Invalid image format");
|
||||
}
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
|
||||
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(w);
|
||||
tuple->items[1] = MP_OBJ_NEW_SMALL_INT(h);
|
||||
tuple->items[2] = mp_obj_new_bool(grayscale);
|
||||
return MP_OBJ_FROM_PTR(tuple);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorui_Display_toif_info_obj,
|
||||
mod_trezorui_Display_toif_info);
|
||||
|
||||
/// def image(self, x: int, y: int, image: bytes) -> None:
|
||||
/// """
|
||||
/// Renders an image at position (x,y).
|
||||
@ -131,16 +158,15 @@ STATIC mp_obj_t mod_trezorui_Display_image(size_t n_args,
|
||||
mp_buffer_info_t image = {0};
|
||||
mp_get_buffer_raise(args[3], &image, MP_BUFFER_READ);
|
||||
const uint8_t *data = image.buf;
|
||||
if (image.len < 8 || memcmp(data, "TOIf", 4) != 0) {
|
||||
|
||||
uint16_t w = 0;
|
||||
uint16_t h = 0;
|
||||
bool grayscale = false;
|
||||
bool valid = display_toif_info(data, image.len, &w, &h, &grayscale);
|
||||
if (!valid || grayscale) {
|
||||
mp_raise_ValueError("Invalid image format");
|
||||
}
|
||||
mp_int_t w = *(uint16_t *)(data + 4);
|
||||
mp_int_t h = *(uint16_t *)(data + 6);
|
||||
uint32_t datalen = *(uint32_t *)(data + 8);
|
||||
if (datalen != image.len - 12) {
|
||||
mp_raise_ValueError("Invalid size of data");
|
||||
}
|
||||
display_image(x, y, w, h, data + 12, datalen);
|
||||
display_image(x, y, w, h, data + 12, image.len - 12);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_image_obj, 4, 4,
|
||||
@ -162,21 +188,20 @@ STATIC mp_obj_t mod_trezorui_Display_avatar(size_t n_args,
|
||||
mp_buffer_info_t image = {0};
|
||||
mp_get_buffer_raise(args[3], &image, MP_BUFFER_READ);
|
||||
const uint8_t *data = image.buf;
|
||||
if (image.len < 8 || memcmp(data, "TOIf", 4) != 0) {
|
||||
|
||||
uint16_t w = 0;
|
||||
uint16_t h = 0;
|
||||
bool grayscale = false;
|
||||
bool valid = display_toif_info(data, image.len, &w, &h, &grayscale);
|
||||
if (!valid || grayscale) {
|
||||
mp_raise_ValueError("Invalid image format");
|
||||
}
|
||||
mp_int_t w = *(uint16_t *)(data + 4);
|
||||
mp_int_t h = *(uint16_t *)(data + 6);
|
||||
if (w != AVATAR_IMAGE_SIZE || h != AVATAR_IMAGE_SIZE) {
|
||||
mp_raise_ValueError("Invalid image size");
|
||||
}
|
||||
uint32_t datalen = *(uint32_t *)(data + 8);
|
||||
if (datalen != image.len - 12) {
|
||||
mp_raise_ValueError("Invalid size of data");
|
||||
}
|
||||
mp_int_t fgcolor = mp_obj_get_int(args[4]);
|
||||
mp_int_t bgcolor = mp_obj_get_int(args[5]);
|
||||
display_avatar(x, y, data + 12, datalen, fgcolor, bgcolor);
|
||||
display_avatar(x, y, data + 12, image.len - 12, fgcolor, bgcolor);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_avatar_obj, 6,
|
||||
@ -196,15 +221,14 @@ STATIC mp_obj_t mod_trezorui_Display_icon(size_t n_args, const mp_obj_t *args) {
|
||||
mp_buffer_info_t icon = {0};
|
||||
mp_get_buffer_raise(args[3], &icon, MP_BUFFER_READ);
|
||||
const uint8_t *data = icon.buf;
|
||||
if (icon.len < 8 || memcmp(data, "TOIg", 4) != 0) {
|
||||
|
||||
uint16_t w = 0;
|
||||
uint16_t h = 0;
|
||||
bool grayscale = false;
|
||||
bool valid = display_toif_info(data, icon.len, &w, &h, &grayscale);
|
||||
if (!valid || !grayscale) {
|
||||
mp_raise_ValueError("Invalid image format");
|
||||
}
|
||||
mp_int_t w = *(uint16_t *)(data + 4);
|
||||
mp_int_t h = *(uint16_t *)(data + 6);
|
||||
uint32_t datalen = *(uint32_t *)(data + 8);
|
||||
if (datalen != icon.len - 12) {
|
||||
mp_raise_ValueError("Invalid size of data");
|
||||
}
|
||||
mp_int_t fgcolor = mp_obj_get_int(args[4]);
|
||||
mp_int_t bgcolor = mp_obj_get_int(args[5]);
|
||||
display_icon(x, y, w, h, data + 12, icon.len - 12, fgcolor, bgcolor);
|
||||
@ -545,6 +569,8 @@ STATIC const mp_rom_map_elem_t mod_trezorui_Display_locals_dict_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR_bar), MP_ROM_PTR(&mod_trezorui_Display_bar_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_bar_radius),
|
||||
MP_ROM_PTR(&mod_trezorui_Display_bar_radius_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_toif_info),
|
||||
MP_ROM_PTR(&mod_trezorui_Display_toif_info_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_image), MP_ROM_PTR(&mod_trezorui_Display_image_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_avatar), MP_ROM_PTR(&mod_trezorui_Display_avatar_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_icon), MP_ROM_PTR(&mod_trezorui_Display_icon_obj)},
|
||||
|
@ -50,6 +50,13 @@ class Display:
|
||||
are drawn with radius radius.
|
||||
"""
|
||||
|
||||
def toif_info(self, image: bytes) -> Tuple[int, int, bool]:
|
||||
"""
|
||||
Returns tuple containing TOIF image dimensions: width, height, and
|
||||
whether it is grayscale.
|
||||
Raises an exception for corrupted images.
|
||||
"""
|
||||
|
||||
def image(self, x: int, y: int, image: bytes) -> None:
|
||||
"""
|
||||
Renders an image at position (x,y).
|
||||
|
@ -12,6 +12,27 @@ if False:
|
||||
from trezor.messages.ApplySettings import ApplySettings, EnumTypeSafetyCheckLevel
|
||||
|
||||
|
||||
def validate_homescreen(homescreen: bytes) -> None:
|
||||
if homescreen == b"":
|
||||
return
|
||||
|
||||
if len(homescreen) > storage.device.HOMESCREEN_MAXSIZE:
|
||||
raise wire.DataError(
|
||||
"Homescreen is too large, maximum size is {} bytes".format(
|
||||
storage.device.HOMESCREEN_MAXSIZE
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
w, h, grayscale = ui.display.toif_info(homescreen)
|
||||
except ValueError:
|
||||
raise wire.DataError("Invalid homescreen")
|
||||
if w != 144 or h != 144:
|
||||
raise wire.DataError("Homescreen must be 144x144 pixel large")
|
||||
if grayscale:
|
||||
raise wire.DataError("Homescreen must be full-color TOIF image")
|
||||
|
||||
|
||||
async def apply_settings(ctx: wire.Context, msg: ApplySettings):
|
||||
if not storage.device.is_initialized():
|
||||
raise wire.NotInitialized("Device is not initialized")
|
||||
@ -27,8 +48,7 @@ async def apply_settings(ctx: wire.Context, msg: ApplySettings):
|
||||
raise wire.ProcessError("No setting provided")
|
||||
|
||||
if msg.homescreen is not None:
|
||||
if len(msg.homescreen) > storage.device.HOMESCREEN_MAXSIZE:
|
||||
raise wire.DataError("Homescreen is too complex")
|
||||
validate_homescreen(msg.homescreen)
|
||||
await require_confirm_change_homescreen(ctx)
|
||||
try:
|
||||
storage.device.set_homescreen(msg.homescreen)
|
||||
|
@ -138,10 +138,7 @@ def get_homescreen() -> Optional[bytes]:
|
||||
def set_homescreen(homescreen: bytes) -> None:
|
||||
if len(homescreen) > HOMESCREEN_MAXSIZE:
|
||||
raise ValueError # homescreen too large
|
||||
if homescreen[:8] == b"TOIf\x90\x00\x90\x00" or homescreen == b"":
|
||||
common.set(_NAMESPACE, _HOMESCREEN, homescreen, public=True)
|
||||
else:
|
||||
raise ValueError # invalid homescreen
|
||||
common.set(_NAMESPACE, _HOMESCREEN, homescreen, public=True)
|
||||
|
||||
|
||||
def store_mnemonic_secret(
|
||||
|
@ -115,6 +115,39 @@ class TestMsgApplysettings:
|
||||
assert client.features.passphrase_protection is True
|
||||
assert client.features.passphrase_always_on_device is False
|
||||
|
||||
@pytest.mark.skip_t1
|
||||
def test_apply_homescreen_toif(self, client):
|
||||
img = b"TOIf\x90\x00\x90\x00~\x00\x00\x00\xed\xd2\xcb\r\x83@\x10D\xc1^.\xde#!\xac31\x99\x10\x8aC%\x14~\x16\x92Y9\x02WI3\x01<\xf5cI2d\x1es(\xe1[\xdbn\xba\xca\xe8s7\xa4\xd5\xd4\xb3\x13\xbdw\xf6:\xf3\xd1\xe7%\xc7]\xdd_\xb3\x9e\x9f\x9e\x9fN\xed\xaaE\xef\xdc\xcf$D\xa7\xa4X\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0OV"
|
||||
|
||||
with client:
|
||||
_set_expected_responses(client)
|
||||
device.apply_settings(client, homescreen=img)
|
||||
|
||||
client.set_expected_responses(EXPECTED_RESPONSES_NOPIN)
|
||||
device.apply_settings(client, homescreen=b"")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"toif_data",
|
||||
[
|
||||
# incomplete header
|
||||
b"TOIf\x90\00\x90\x00~"
|
||||
# wrong magic
|
||||
b"XXXf\x90\x00\x90\x00~\x00\x00\x00\xed\xd2\xcb\r\x83@\x10D\xc1^.\xde#!\xac31\x99\x10\x8aC%\x14~\x16\x92Y9\x02WI3\x01<\xf5cI2d\x1es(\xe1[\xdbn\xba\xca\xe8s7\xa4\xd5\xd4\xb3\x13\xbdw\xf6:\xf3\xd1\xe7%\xc7]\xdd_\xb3\x9e\x9f\x9e\x9fN\xed\xaaE\xef\xdc\xcf$D\xa7\xa4X\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0OV"
|
||||
# wrong datasize in header
|
||||
b"TOIf\x90\x00\x90\x00~\x00\x00\x00\xed\xd2\xcb\r\x83@\x10D\xc1^.\xde#!\xac31\x99\x10\x8aC%\x14~\x16\x92Y9\x02WI3\x01<\xf5cI2d\x1es(\xe1[\xdbn\xba\xca\xe8s7\xa4\xd5\xd4\xb3\x13\xbdw\xf6:\xf3\xd1\xe7%\xc7]\xdd_\xb3\x9e\x9f\x9e\x9fN\xed\xaaE\xef\xdc\xcf$D\xa7\xa4X\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
# grayscale 144x144
|
||||
b"TOIg\x90\x00\x90\x00~\x00\x00\x00\xed\xd2\xcb\r\x83@\x10D\xc1^.\xde#!\xac31\x99\x10\x8aC%\x14~\x16\x92Y9\x02WI3\x01<\xf5cI2d\x1es(\xe1[\xdbn\xba\xca\xe8s7\xa4\xd5\xd4\xb3\x13\xbdw\xf6:\xf3\xd1\xe7%\xc7]\xdd_\xb3\x9e\x9f\x9e\x9fN\xed\xaaE\xef\xdc\xcf$D\xa7\xa4X\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0OV",
|
||||
# fullcolor 128x128
|
||||
b"TOIf\x80\x00\x80\x00~\x00\x00\x00\xed\xd2\xcb\r\x83@\x10D\xc1^.\xde#!\xac31\x99\x10\x8aC%\x14~\x16\x92Y9\x02WI3\x01<\xf5cI2d\x1es(\xe1[\xdbn\xba\xca\xe8s7\xa4\xd5\xd4\xb3\x13\xbdw\xf6:\xf3\xd1\xe7%\xc7]\xdd_\xb3\x9e\x9f\x9e\x9fN\xed\xaaE\xef\xdc\xcf$D\xa7\xa4X\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0OV",
|
||||
],
|
||||
)
|
||||
@pytest.mark.skip_ui
|
||||
@pytest.mark.skip_t1
|
||||
def test_apply_homescreen_toif_fail(self, client, toif_data):
|
||||
with pytest.raises(exceptions.TrezorFailure), client:
|
||||
client.use_pin_sequence([PIN4])
|
||||
device.apply_settings(client, homescreen=toif_data)
|
||||
|
||||
@pytest.mark.skip_t2
|
||||
def test_apply_homescreen(self, client):
|
||||
img = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\x04\x80\x00\x00\x00\x00\x00\x00\x00\x00\x04\x88\x02\x00\x00\x00\x02\x91\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x90@\x00\x11@\x00\x00\x00\x00\x00\x00\x08\x00\x10\x92\x12\x04\x00\x00\x05\x12D\x00\x00\x00\x00\x00 \x00\x00\x08\x00Q\x00\x00\x02\xc0\x00\x00\x00\x00\x00\x00\x00\x10\x02 \x01\x04J\x00)$\x00\x00\x00\x00\x80\x00\x00\x00\x00\x08\x10\xa1\x00\x00\x02\x81 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\tP\x00\x00\x00\x00\x00\x00 \x00\x00\xa0\x00\xa0R \x12\x84\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x08\x00\tP\x00\x00\x00\x00 \x00\x04 \x00\x80\x02\x00@\x02T\xc2 \x00\x00\x00\x00\x00\x00\x00\x10@\x00)\t@\n\xa0\x80\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x80@\x14\xa9H\x04\x00\x00\x88@\x00\x00\x00\x00\x00\x02\x02$\x00\x15B@\x00\nP\x00\x00\x00\x00\x00\x80\x00\x00\x91\x01UP\x00\x00 \x02\x00\x00\x00\x00\x00\x00\x02\x08@ Z\xa5 \x00\x00\x80\x00\x00\x00\x00\x00\x00\x08\xa1%\x14*\xa0\x00\x00\x02\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00@\xaa\x91 \x00\x05E\x80\x00\x00\x00\x00\x00\x02*T\x05-D\x00\x00\x05 @\x00\x00\x00\x00\x00%@\x80\x11V\xa0\x88\x00\x05@\xb0\x00\x00\x00\x00\x00\x818$\x04\xabD \x00\x06\xa1T\x00\x00\x00\x00\x02\x03\xb8\x01R\xd5\x01\x00\x00\x05AP\x00\x00\x00\x00\x08\xadT\x00\x05j\xa4@\x00\x87ah\x00\x00\x00\x00\x02\x8d\xb8\x08\x00.\x01\x00\x00\x02\xa5\xa8\x10\x00\x00\x00*\xc1\xec \n\xaa\x88 \x02@\xf6\xd0\x02\x00\x00\x00\x0bB\xb6\x14@U"\x80\x00\x01{`\x00\x00\x00\x00M\xa3\xf8 \x15*\x00\x00\x00\x10n\xc0\x04\x00\x00\x02\x06\xc2\xa8)\x00\x96\x84\x80\x00\x00\x1b\x00\x00\x80@\x10\x87\xa7\xf0\x84\x10\xaa\x10\x00\x00D\x00\x00\x02 \x00\x8a\x06\xfa\xe0P\n-\x02@\x00\x12\x00\x00\x00\x00\x10@\x83\xdf\xa0\x00\x08\xaa@\x00\x00\x01H\x00\x05H\x04\x12\x01\xf7\x81P\x02T\t\x00\x00\x00 \x00\x00\x84\x10\x00\x00z\x00@)* \x00\x00\x01\n\xa0\x02 \x05\n\x00\x00\x05\x10\x84\xa8\x84\x80\x00\x00@\x14\x00\x92\x10\x80\x00\x04\x11@\tT\x00\x00\x00\x00\n@\x00\x08\x84@$\x00H\x00\x12Q\x02\x00\x00\x00\x00\x90\x02A\x12\xa8\n\xaa\x92\x10\x04\xa8\x10@\x00\x00\x04\x04\x00\x04I\x00\x04\x14H\x80"R\x01\x00\x00\x00!@\x00\x00$\xa0EB\x80\x08\x95hH\x00\x00\x00\x84\x10 \x05Z\x00\x00(\x00\x02\x00\xa1\x01\x00\x00\x04\x00@\x82\x00\xadH*\x92P\x00\xaaP\x00\x00\x00\x00\x11\x02\x01*\xad\x01\x00\x01\x01"\x11D\x08\x00\x00\x10\x80 \x00\x81W\x80J\x94\x04\x08\xa5 !\x00\x00\x00\x02\x00B*\xae\xa1\x00\x80\x10\x01\x08\xa4\x00\x00\x00\x00\x00\x84\x00\t[@"HA\x04E\x00\x84\x00\x00\x00\x10\x00\x01J\xd5\x82\x90\x02\x00!\x02\xa2\x00\x00\x00\x00\x00\x00\x00\x05~\xa0\x00 \x10\n)\x00\x11\x00\x00\x00\x00\x00\x00!U\x80\xa8\x88\x82\x80\x01\x00\x00\x00\x00\x00\x00H@\x11\xaa\xc0\x82\x00 *\n\x00\x00\x00\x00\x00\x00\x00\x00\n\xabb@ \x04\x00! \x84\x00\x00\x00\x00\x02@\xa5\x15A$\x04\x81(\n\x00\x00\x00\x00\x00\x00 \x01\x10\x02\xe0\x91\x02\x00\x00\x04\x00\x00\x00\x00\x00\x00\x01 \xa9\tQH@\x91 P\x00\x00\x00\x00\x00\x00\x08\x00\x00\xa0T\xa5\x00@\x80\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\xa2\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00 T\xa0\t\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00@\x02\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x10\x00\x00\x10\x02\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00@\x04\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x08@\x10\x00\x00\x00\x00'
|
||||
|
@ -11,6 +11,7 @@
|
||||
"test_basic.py-test_device_id_same": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
||||
"test_basic.py-test_features": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
||||
"test_basic.py-test_ping": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
||||
"test_msg_applysettings.py-test_apply_homescreen_toif": "d129829fe8c6c30125151b134826b6a11eb96cca2158c1cad951290ac314a5cc",
|
||||
"test_msg_applysettings.py-test_apply_settings": "2cc8bf660f3be815d19a4bf1265936162a58386fbe632ca4be01541245b79134",
|
||||
"test_msg_applysettings.py-test_apply_settings_passphrase": "5c1ed9a0be3d14475102d447da0b5d51bbb6dfaaeceff5ea9179064609db7870",
|
||||
"test_msg_applysettings.py-test_apply_settings_passphrase_on_device": "3e6527e227bdde54f51bc9c417b176d0d87fdb6c40c4761368f50eb201b4beed",
|
||||
|
Loading…
Reference in New Issue
Block a user