Skip to content

Commit

Permalink
Adds "screen saver" mode to signer app (#190)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
italo-sampaio authored Aug 21, 2024
1 parent 85c304d commit 6bbebfa
Show file tree
Hide file tree
Showing 14 changed files with 567 additions and 92 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions firmware/coverage/gen-coverage
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
104 changes: 12 additions & 92 deletions firmware/src/ledger/signer/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
77 changes: 77 additions & 0 deletions firmware/src/ledger/signer/src/signer_ux.c
Original file line number Diff line number Diff line change
@@ -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();
}
55 changes: 55 additions & 0 deletions firmware/src/ledger/signer/src/signer_ux.h
Original file line number Diff line number Diff line change
@@ -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
64 changes: 64 additions & 0 deletions firmware/src/ledger/signer/src/signer_ux_info.c
Original file line number Diff line number Diff line change
@@ -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 <stddef.h>
#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);
}
Loading

0 comments on commit 6bbebfa

Please sign in to comment.