From 6bbebfa91603884e517f64faf39e3ce07f786ba9 Mon Sep 17 00:00:00 2001 From: Italo Sampaio <100376888+italo-sampaio@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:21:08 -0300 Subject: [PATCH] Adds "screen saver" mode to signer app (#190) Adds a "screen saver" mode that turns off the display after 30 seconds of inactivity in the Signer app. The goal is to extend the Ledger screen lifetime by reducing the time the display shows a static screen uninterruptedly. - Added new `ui_screensaver` screen to Signer app - Factored out Signer UX code into testable units - Added unit tests to the Signer app - Added signer ux code to coverage report --- .github/workflows/run-tests.yml | 3 + QUICKSTART.md | 1 + firmware/coverage/gen-coverage | 1 + firmware/src/ledger/signer/src/main.c | 104 ++----------- firmware/src/ledger/signer/src/signer_ux.c | 77 ++++++++++ firmware/src/ledger/signer/src/signer_ux.h | 55 +++++++ .../src/ledger/signer/src/signer_ux_info.c | 64 ++++++++ .../ledger/signer/src/signer_ux_screensaver.c | 53 +++++++ .../src/ledger/signer/test/mock/common.mk | 30 ++++ firmware/src/ledger/signer/test/mock/mock.h | 68 +++++++++ .../signer/test/mock/os_io_seproxyhal.h | 1 + firmware/src/ledger/signer/test/run-all.sh | 13 ++ .../src/ledger/signer/test/signer_ux/Makefile | 45 ++++++ .../signer/test/signer_ux/test_signer_ux.c | 144 ++++++++++++++++++ 14 files changed, 567 insertions(+), 92 deletions(-) create mode 100644 firmware/src/ledger/signer/src/signer_ux.c create mode 100644 firmware/src/ledger/signer/src/signer_ux.h create mode 100644 firmware/src/ledger/signer/src/signer_ux_info.c create mode 100644 firmware/src/ledger/signer/src/signer_ux_screensaver.c create mode 100644 firmware/src/ledger/signer/test/mock/common.mk create mode 100644 firmware/src/ledger/signer/test/mock/mock.h create mode 120000 firmware/src/ledger/signer/test/mock/os_io_seproxyhal.h create mode 100755 firmware/src/ledger/signer/test/run-all.sh create mode 100644 firmware/src/ledger/signer/test/signer_ux/Makefile create mode 100644 firmware/src/ledger/signer/test/signer_ux/test_signer_ux.c diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 90c441bb..336d189d 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -35,6 +35,9 @@ jobs: - name: Ledger UI's unit tests run: firmware/src/ledger/ui/test/run-all.sh + - name: Ledger Signer's unit tests + run: firmware/src/ledger/signer/test/run-all.sh + run-integration-tests: name: Integration tests runs-on: ubuntu-20.04 diff --git a/QUICKSTART.md b/QUICKSTART.md index 41c5a3cd..4406a373 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -24,6 +24,7 @@ Unless otherwise stated, only x86 platforms are supported for building this proj ~/repo> middleware/test-all # Middleware unit tests ~/repo> firmware/test/test-all # Firmware tests ~/repo> firmware/src/ledger/ui/test/run-all.sh # Ledger UI application unit tests +~/repo> firmware/src/ledger/signer/test/run-all.sh # Ledger Signer application unit tests ~/repo> firmware/src/common/test/run-all.sh # Common code unit tests ~/repo> firmware/src/powhsm/test/run-all.sh # powHSM logic unit tests ~/repo> firmware/src/hal/test/run-all.sh # HAL unit tests diff --git a/firmware/coverage/gen-coverage b/firmware/coverage/gen-coverage index c0cc2072..210004a0 100755 --- a/firmware/coverage/gen-coverage +++ b/firmware/coverage/gen-coverage @@ -13,6 +13,7 @@ if [[ $1 == "exec" ]]; then COVERAGE=y $REPOROOT/firmware/src/common/test/run-all.sh COVERAGE=y $REPOROOT/firmware/src/powhsm/test/run-all.sh COVERAGE=y $REPOROOT/firmware/src/ledger/ui/test/run-all.sh + COVERAGE=y $REPOROOT/firmware/src/ledger/signer/test/run-all.sh COVERAGE=y $REPOROOT/firmware/src/tcpsigner/test/run-all.sh # Run tcpsigner test suite diff --git a/firmware/src/ledger/signer/src/main.c b/firmware/src/ledger/signer/src/main.c index b287ca13..4f4e15ab 100644 --- a/firmware/src/ledger/signer/src/main.c +++ b/firmware/src/ledger/signer/src/main.c @@ -43,69 +43,21 @@ #include "os_io_seproxyhal.h" #include "hsm.h" +#include "signer_ux.h" // HAL includes #include "hal/communication.h" -unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; - -static unsigned int current_text_pos; // parsing cursor in the text to display +// The interval between two subsequent ticker events in milliseconds. This is +// assumed to be 100ms according to the nanos-secure-sdk documentation. +#define TICKER_INTERVAL_MS 100 +// The time in milliseconds after which the screen saver is displayed if no +// button is pressed +#define SCREEN_SAVER_TIMEOUT_MS 30000 -// UI currently displayed -enum UI_STATE { UI_IDLE, UI_TEXT, UI_APPROVAL }; -enum UI_STATE uiState; +unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; ux_state_t ux; -static void ui_idle(void); -static unsigned char display_text_part(void); - -#define MAX_CHARS_PER_LINE 19 -#define DEFAULT_FONT BAGL_FONT_OPEN_SANS_LIGHT_16px | BAGL_FONT_ALIGNMENT_LEFT -#define TEXT_HEIGHT 15 -#define TEXT_SPACE 4 - -static char lineBuffer[MAX_CHARS_PER_LINE + 1]; - -// clang-format off -static const bagl_element_t bagl_ui_idle_nanos[] = { - { - {BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, - 0xFFFFFF, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL, - }, - { - {BAGL_LABELINE, 0x02, 0, 12, 128, 11, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Signer running...", - 0, - 0, - 0, - NULL, - NULL, - NULL, - } -}; -// clang-format on - -static unsigned int bagl_ui_idle_nanos_button( - unsigned int button_mask, unsigned int button_mask_counter) { - switch (button_mask) { - case BUTTON_EVT_RELEASED | BUTTON_LEFT: - case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: - // We removed this function, but leave it in src in case its needed - // for debug. io_seproxyhal_touch_exit(NULL); - break; - } - - return 0; -} - unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { switch (channel & ~(IO_FLAGS)) { case CHANNEL_KEYBOARD: @@ -136,31 +88,6 @@ void io_seproxyhal_display(const bagl_element_t *element) { io_seproxyhal_display_default((bagl_element_t *)element); } -// Pick the text elements to display -static unsigned char display_text_part() { - unsigned int i; - WIDE char *text = (char *)G_io_apdu_buffer + 5; - if (text[current_text_pos] == '\0') { - return 0; - } - i = 0; - while ((text[current_text_pos] != 0) && (text[current_text_pos] != '\n') && - (i < MAX_CHARS_PER_LINE)) { - lineBuffer[i++] = text[current_text_pos]; - current_text_pos++; - } - if (text[current_text_pos] == '\n') { - current_text_pos++; - } - lineBuffer[i] = '\0'; - return 1; -} - -static void ui_idle(void) { - uiState = UI_IDLE; - UX_DISPLAY(bagl_ui_idle_nanos, NULL); -} - unsigned char io_event(unsigned char channel) { // nothing done with the event, throw an error on the transport layer if // needed @@ -173,26 +100,19 @@ unsigned char io_event(unsigned char channel) { case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: // for Nano S UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); + signer_ux_handle_button_press(); break; case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT: - if ((uiState == UI_TEXT) && - (os_seph_features() & - SEPROXYHAL_TAG_SESSION_START_EVENT_FEATURE_SCREEN_BIG)) { - if (display_text_part()) { - UX_REDISPLAY(); - } - } else { - UX_DISPLAYED_EVENT(); - } + UX_DISPLAYED_EVENT(); break; case SEPROXYHAL_TAG_TICKER_EVENT: UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, { // defaulty retrig very soon (will be overriden during // stepper_prepro) UX_CALLBACK_SET_INTERVAL(500); - UX_REDISPLAY(); }); + signer_ux_handle_ticker_event(TICKER_INTERVAL_MS); break; // unknown events are acknowledged @@ -262,7 +182,7 @@ __attribute__((section(".boot"))) int main(int argc, char **argv) { USB_power(0); USB_power(1); - ui_idle(); + signer_ux_init(SCREEN_SAVER_TIMEOUT_MS); // next timer callback in 500 ms UX_CALLBACK_SET_INTERVAL(500); diff --git a/firmware/src/ledger/signer/src/signer_ux.c b/firmware/src/ledger/signer/src/signer_ux.c new file mode 100644 index 00000000..aba37ad9 --- /dev/null +++ b/firmware/src/ledger/signer/src/signer_ux.c @@ -0,0 +1,77 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "signer_ux.h" + +// UI currently displayed +enum UI_STATE { UI_INFO, UI_SCREENSAVER }; +enum UI_STATE G_ui_state; +// Time spent since the last button press +static unsigned int G_idle_time_ms; +// The time in milliseconds after which the screen saver is displayed if no +// button is pressed +static unsigned int G_screensaver_timeout_ms; + +// Local functions +static void signer_ux_update(void) { + switch (G_ui_state) { + case UI_INFO: + if (G_idle_time_ms >= G_screensaver_timeout_ms) { + G_ui_state = UI_SCREENSAVER; + signer_ux_screensaver(); + } + break; + case UI_SCREENSAVER: + if (G_idle_time_ms < G_screensaver_timeout_ms) { + G_ui_state = UI_INFO; + signer_ux_info(); + } + break; + default: + break; + } +} + +// Public interface +void signer_ux_init(unsigned int screensaver_timeout_ms) { + G_idle_time_ms = 0; + G_ui_state = UI_INFO; + G_screensaver_timeout_ms = screensaver_timeout_ms; + signer_ux_info(); +} + +void signer_ux_handle_button_press(void) { + G_idle_time_ms = 0; + signer_ux_update(); +} + +void signer_ux_handle_ticker_event(unsigned int interval_ms) { + unsigned int last_idle_time_ms = G_idle_time_ms; + G_idle_time_ms += interval_ms; + // Handle overflow + if (G_idle_time_ms < last_idle_time_ms) { + G_idle_time_ms = last_idle_time_ms; + } + signer_ux_update(); +} diff --git a/firmware/src/ledger/signer/src/signer_ux.h b/firmware/src/ledger/signer/src/signer_ux.h new file mode 100644 index 00000000..f0645b6c --- /dev/null +++ b/firmware/src/ledger/signer/src/signer_ux.h @@ -0,0 +1,55 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __SIGNER_UX_H +#define __SIGNER_UX_H + +/* + * Initialize the UX state. + * + * @arg[in] screensaver_timeout_ms The time after which the screen saver is + * displayed if no button is pressed + */ +void signer_ux_init(unsigned int screensaver_timeout_ms); + +/* + * Handle a button press event. + * + * This function should be called whenever a button is pressed. + */ +void signer_ux_handle_button_press(void); +/* + * Handles a ticker event. + * + * Increments the internal timer and updates the UI state if necessary. + * + * @arg[in] interval_ms Time spent since last ticker event + */ +void signer_ux_handle_ticker_event(unsigned int interval_ms); + +// all screens +void signer_ux_info(void); +void signer_ux_screensaver(void); + +#endif // __SIGNER_UX_H diff --git a/firmware/src/ledger/signer/src/signer_ux_info.c b/firmware/src/ledger/signer/src/signer_ux_info.c new file mode 100644 index 00000000..2920477a --- /dev/null +++ b/firmware/src/ledger/signer/src/signer_ux_info.c @@ -0,0 +1,64 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include "os_io_seproxyhal.h" +#include "signer_ux.h" + +// clang-format off +static const bagl_element_t bagl_ui_info_nanos[] = { + { + {BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, + 0xFFFFFF, 0, 0}, + NULL, + 0, + 0, + 0, + NULL, + NULL, + NULL, + }, + { + {BAGL_LABELINE, 0x02, 0, 12, 128, 11, 0, 0, 0, 0xFFFFFF, 0x000000, + BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, + "Signer running...", + 0, + 0, + 0, + NULL, + NULL, + NULL, + } +}; +// clang-format on + +static unsigned int bagl_ui_info_nanos_button( + unsigned int button_mask, unsigned int button_mask_counter) { + // no-op - button presses are handled directly in the event loop + return 0; +} + +void signer_ux_info(void) { + UX_DISPLAY(bagl_ui_info_nanos, NULL); +} diff --git a/firmware/src/ledger/signer/src/signer_ux_screensaver.c b/firmware/src/ledger/signer/src/signer_ux_screensaver.c new file mode 100644 index 00000000..19a7c63d --- /dev/null +++ b/firmware/src/ledger/signer/src/signer_ux_screensaver.c @@ -0,0 +1,53 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include "os_io_seproxyhal.h" +#include "signer_ux.h" + +// clang-format off +static const bagl_element_t bagl_ui_screensaver_nanos[] = { + { + {BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, + 0x000000, 0, 0}, + NULL, + 0, + 0, + 0, + NULL, + NULL, + NULL, + }, +}; +// clang-format on + +static unsigned int bagl_ui_screensaver_nanos_button( + unsigned int button_mask, unsigned int button_mask_counter) { + // no-op - button presses are handled directly in the event loop + return 0; +} + +void signer_ux_screensaver(void) { + UX_DISPLAY(bagl_ui_screensaver_nanos, NULL); +} diff --git a/firmware/src/ledger/signer/test/mock/common.mk b/firmware/src/ledger/signer/test/mock/common.mk new file mode 100644 index 00000000..6b46621e --- /dev/null +++ b/firmware/src/ledger/signer/test/mock/common.mk @@ -0,0 +1,30 @@ +# The MIT License (MIT) +# +# Copyright (c) 2021 RSK Labs Ltd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +SRCDIR = ../../src +COMMONDIR = ../../../../common/src +MOCKDIR = ../mock +THISMOCKDIR = ./mock +CFLAGS = -iquote $(THISMOCKDIR) -iquote $(MOCKDIR) +CFLAGS += -iquote $(SRCDIR) + +include ../../../../../coverage/coverage.mk \ No newline at end of file diff --git a/firmware/src/ledger/signer/test/mock/mock.h b/firmware/src/ledger/signer/test/mock/mock.h new file mode 100644 index 00000000..524b0080 --- /dev/null +++ b/firmware/src/ledger/signer/test/mock/mock.h @@ -0,0 +1,68 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __SIGNER_MOCK_H +#define __SIGNER_MOCK_H + +// Nano S SDK constants used by the signer ux components +#define BAGL_FILL 1 +#define BAGL_RECTANGLE 3 +#define BAGL_LABELINE 7 +#define BAGL_FONT_OPEN_SANS_REGULAR_11px 10 +#define BAGL_FONT_ALIGNMENT_CENTER 0x8000 + +// Nano S SDK types used by the signer ux components +typedef struct { + unsigned int type; + unsigned char userid; + short x; + short y; + unsigned short width; + unsigned short height; + unsigned char stroke; + unsigned char radius; + unsigned char fill; + unsigned int fgcolor; + unsigned int bgcolor; + unsigned short font_id; + unsigned char icon_id; +} mock_signer_ux_component_t; + +typedef struct { + mock_signer_ux_component_t component; + const char *text; + unsigned char touch_area_brim; + int overfgcolor; + int overbgcolor; + const void *tap; + const void *out; + const void *over; +} mock_signer_ux_element_t; + +typedef mock_signer_ux_element_t bagl_element_t; + +// Nano S SDK functions and macros used by the signer ux components +void UX_DISPLAY(const mock_signer_ux_element_t *elements_array, void *callback); + +#endif // __SIGNER_MOCK_H diff --git a/firmware/src/ledger/signer/test/mock/os_io_seproxyhal.h b/firmware/src/ledger/signer/test/mock/os_io_seproxyhal.h new file mode 120000 index 00000000..8b6f7d95 --- /dev/null +++ b/firmware/src/ledger/signer/test/mock/os_io_seproxyhal.h @@ -0,0 +1 @@ +../../../ui/test/mock/os_io_seproxyhal.h \ No newline at end of file diff --git a/firmware/src/ledger/signer/test/run-all.sh b/firmware/src/ledger/signer/test/run-all.sh new file mode 100755 index 00000000..de927848 --- /dev/null +++ b/firmware/src/ledger/signer/test/run-all.sh @@ -0,0 +1,13 @@ +#!/bin/bash +BASEDIR=$(dirname $0) +TESTDIRS="signer_ux" +TESTDIRS=${1:-"$TESTDIRS"} + +for d in $TESTDIRS; do + echo "******************************" + echo "Testing $d..." + echo "******************************" + cd "$BASEDIR/$d" + make clean test || exit $? + cd - > /dev/null +done diff --git a/firmware/src/ledger/signer/test/signer_ux/Makefile b/firmware/src/ledger/signer/test/signer_ux/Makefile new file mode 100644 index 00000000..502447fc --- /dev/null +++ b/firmware/src/ledger/signer/test/signer_ux/Makefile @@ -0,0 +1,45 @@ +# The MIT License (MIT) +# +# Copyright (c) 2021 RSK Labs Ltd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +include ../mock/common.mk + +PROG = test.out +OBJS = signer_ux.o signer_ux_info.o signer_ux_screensaver.o test_signer_ux.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(COVFLAGS) -o $@ $^ -g + +test_signer_ux.o: test_signer_ux.c + $(CC) $(CFLAGS) -c -o $@ $^ + +%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) $(COVFLAGS) -c -o $@ $^ + +.PHONY: clean test + +clean: + rm -f $(PROG) *.o $(COVFILES) + +test: all + ./$(PROG) diff --git a/firmware/src/ledger/signer/test/signer_ux/test_signer_ux.c b/firmware/src/ledger/signer/test/signer_ux/test_signer_ux.c new file mode 100644 index 00000000..8a426c2b --- /dev/null +++ b/firmware/src/ledger/signer/test/signer_ux/test_signer_ux.c @@ -0,0 +1,144 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include "mock.h" +#include "signer_ux.h" + +static unsigned int G_mock_screensaver_timeout_ms; + +// The arguments passed to the last call to UX_DISPLAY +static mock_signer_ux_element_t *G_elements_array_arg; +static void *G_callback_arg; +// Mock implementation of UX_DISPLAY +void UX_DISPLAY(const mock_signer_ux_element_t *elements_array, + void *callback) { + G_elements_array_arg = (mock_signer_ux_element_t *)elements_array; + G_callback_arg = callback; +} + +// Helper functions +// These functions simply assert that the last call to UX_DISPLAY was made +// with the expected arguments +static void assert_ui_info() { + assert(G_elements_array_arg != NULL); + assert(G_callback_arg == NULL); + assert(G_elements_array_arg[0].component.type == BAGL_RECTANGLE); + assert(G_elements_array_arg[0].component.width == 128); + assert(G_elements_array_arg[0].component.height == 32); + assert(G_elements_array_arg[0].component.fgcolor == 0x000000); + assert(G_elements_array_arg[0].component.bgcolor == 0xFFFFFF); + + assert(G_elements_array_arg[1].component.type == BAGL_LABELINE); + assert(G_elements_array_arg[1].component.width == 128); + assert(G_elements_array_arg[1].component.height == 11); + assert(G_elements_array_arg[1].component.fgcolor == 0xFFFFFF); + assert(G_elements_array_arg[1].component.bgcolor == 0x000000); + assert(0 == strcmp(G_elements_array_arg[1].text, "Signer running...")); + + assert(G_callback_arg == NULL); +} + +static void assert_ui_screensaver() { + assert(G_elements_array_arg != NULL); + assert(G_callback_arg == NULL); + assert(G_elements_array_arg[0].component.type == BAGL_RECTANGLE); + assert(G_elements_array_arg[0].component.width == 128); + assert(G_elements_array_arg[0].component.height == 32); + assert(G_elements_array_arg[0].component.stroke == 0x000000); + assert(G_elements_array_arg[0].component.fgcolor == 0x000000); + assert(G_elements_array_arg[0].component.bgcolor == 0x000000); + assert(G_elements_array_arg[0].text == NULL); + + assert(G_callback_arg == NULL); +} + +// Test cases +void setup() { + G_mock_screensaver_timeout_ms = 30000; + G_elements_array_arg = NULL; + G_callback_arg = NULL; + signer_ux_init(G_mock_screensaver_timeout_ms); +} + +void test_init() { + printf("Test init...\n"); + setup(); + assert_ui_info(); +} + +void test_ui_info_ui_screensaver_transition() { + printf("Test transition from UI_INFO to UI_SCREENSAVER...\n"); + setup(); + signer_ux_handle_ticker_event(G_mock_screensaver_timeout_ms - 1); + assert_ui_info(); + signer_ux_handle_ticker_event(1); + assert_ui_screensaver(); +} + +void test_ui_screensaver_ui_info_transition() { + printf("Test transition from UI_SCREENSAVER to UI_INFO...\n"); + setup(); + signer_ux_handle_ticker_event(G_mock_screensaver_timeout_ms); + assert_ui_screensaver(); + signer_ux_handle_button_press(); + assert_ui_info(); +} + +void test_multiple_button_presses() { + printf("Test multiple button presses...\n"); + setup(); + for (int i = 0; i < 100; ++i) { + signer_ux_handle_ticker_event(G_mock_screensaver_timeout_ms - 1); + assert_ui_info(); + signer_ux_handle_button_press(); + assert_ui_info(); + } +} + +void test_timer_overflow() { + printf("Test timer overflow protection...\n"); + setup(); + unsigned int mock_tick_ms = 100; + signer_ux_handle_ticker_event(__UINT32_MAX__ - 100); + assert_ui_screensaver(); + for (int i = 0; i < 1000; i += mock_tick_ms) { + signer_ux_handle_ticker_event(mock_tick_ms); + assert_ui_screensaver(); + } + signer_ux_handle_button_press(); + assert_ui_info(); +} + +int main() { + test_init(); + test_ui_info_ui_screensaver_transition(); + test_ui_screensaver_ui_info_transition(); + test_multiple_button_presses(); + test_timer_overflow(); + + return 0; +}