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; +}