Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release Stax porting #85

Merged
merged 53 commits into from
May 13, 2024
Merged
Changes from 9 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
c7392bb
Move IO functions form main and utils to dedicated io.c file
fbeutin-ledger Mar 23, 2023
66bfff9
Move BAGL UI code for main menu in dedicated file
fbeutin-ledger Mar 23, 2023
96a49a3
Move BAGL UI code for GET_PUBKEY instruction in dedicated file
fbeutin-ledger Mar 23, 2023
60cbae5
Move BAGL UI code for SIGN_MESSAGE instruction in dedicated file
fbeutin-ledger Mar 23, 2023
33ddeb7
Factorize functions in utils
fbeutin-ledger Mar 23, 2023
1105e63
Factorize UX for message and offchain message signing
fbeutin-ledger Mar 23, 2023
1042ade
Lint
fbeutin-ledger Mar 23, 2023
4d90622
Review
fbeutin-ledger Mar 30, 2023
0d590f1
Merge pull request #62 from LedgerHQ/fbe/separate_ui_from_parsing
fbeutin-ledger Jun 6, 2023
4534779
Enable compilation for Stax and port main menu and settings menu
fbeutin-ledger Mar 23, 2023
30761a7
Add Stax screen for GET_PUBLIC_KEY instruction
fbeutin-ledger Mar 23, 2023
a11f329
Add Stax screen for SIGN_MESSAGE instruction
fbeutin-ledger Mar 23, 2023
b00fa8d
Add Stax screen for SIGN_OFFCHAIN_MESSAGE instruction
fbeutin-ledger Mar 23, 2023
60fa447
Lint
fbeutin-ledger Mar 23, 2023
4ea8380
Review
fbeutin-ledger Mar 30, 2023
5f2417d
Update screenshots
fbeutin-ledger Jun 13, 2023
3e6b02b
Update Stax UI following product review
fbeutin-ledger Jun 14, 2023
0630d32
Review
fbeutin-ledger Jun 20, 2023
639bf61
Merge pull request #69 from LedgerHQ/fbe/stax_porting_rebased
fbeutin-ledger Jun 20, 2023
e6a110c
Hotfix Stax compilation
fbeutin-ledger Jun 20, 2023
92d146d
hotfix test snapshots
fbeutin-ledger Jun 20, 2023
715a97a
Add guidelines enforcer
fbeutin-ledger Jun 20, 2023
49226e8
Merge pull request #70 from LedgerHQ/fbe/add_guidelines_enforcer
fbeutin-ledger Jun 20, 2023
c911182
Solana now returns to Exchange after a sign
fbeutin-ledger Jun 20, 2023
e2a9f25
Update LNX snapshots for latest SDK
fbeutin-ledger Jun 26, 2023
532920c
Merge pull request #71 from LedgerHQ/fbe/return_in_exchange
fbeutin-ledger Jun 26, 2023
b6e8825
QR code source path is now used explicitly
apaillier-ledger Aug 3, 2023
e4bcb49
Merge pull request #73 from LedgerHQ/fix/apa/explicit_qrcode_source_path
apaillier-ledger Aug 4, 2023
8438b58
Add a modal when started by Exchange
fbeutin-ledger Aug 29, 2023
24d74f0
Adapt snapshots for latest Stax SDK
fbeutin-ledger Aug 29, 2023
4a99a33
Merge pull request #74 from LedgerHQ/fbe/stax_modal_on_swap
fbeutin-ledger Aug 29, 2023
bd00508
Use new 4bpp icon
fbeutin-ledger Sep 1, 2023
98a22dd
Merge pull request #75 from LedgerHQ/fbe/4bpp_icon
fbeutin-ledger Sep 1, 2023
eb23fcf
Remove unneeded param APPLICATION_FLAG_BOLOS_SETTINGS for Nanos+
fbeutin-ledger Sep 5, 2023
ef1dbca
Merge pull request #76 from LedgerHQ/fbe/fix_app_load_params
fbeutin-ledger Sep 6, 2023
59aa4ba
[auto]: add PR template
sgliner-ledger Nov 29, 2023
da26500
[add] ledger_app.toml manifest
lpascal-ledger Dec 1, 2023
1a17b33
Merge pull request #79 from LedgerHQ/add/manifest
fbeutin-ledger Dec 1, 2023
0b73d10
src: Improve crypto code using SDK crypto_helpers and LEDGER_ASSERT
Dec 12, 2023
ad37ff9
src: Cleanup unused parameter from BLE_power()
Dec 12, 2023
8198345
src: Drop deprectaed check_api_level()
Dec 12, 2023
d4098be
Merge pull request #82 from LedgerHQ/xch/fix-ci-warnings
xchapron-ledger Dec 14, 2023
cfac20e
workflows/sonarcloud.yml: Update workflow following app-bitcoin-new e…
Dec 14, 2023
9f5b1cd
Merge pull request #83 from LedgerHQ/xch/sonarcloud
xchapron-ledger Dec 14, 2023
71d0904
[auto] Update screenshot
Jan 10, 2024
2d1f62f
Merge pull request #84 from LedgerHQ/auto-update-screenshots
sgliner-ledger Jan 11, 2024
33e3cc8
[auto] Update screenshot
Feb 21, 2024
d769b7a
Merge pull request #86 from LedgerHQ/auto-update-screenshots
sgliner-ledger Feb 21, 2024
2d4f805
[auto] Update screenshot
sgliner-ledger Feb 27, 2024
3dfa53e
[auto] Update screenshot
Apr 8, 2024
5c5e1b6
Merge pull request #87 from LedgerHQ/auto-update-screenshots
sgliner-ledger Apr 8, 2024
ce8ca77
Release v1.4.3
fbeutin-ledger Apr 24, 2024
5138865
Merge pull request #88 from LedgerHQ/fbe/release_v1.4.3
fbeutin-ledger Apr 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes
39 changes: 6 additions & 33 deletions src/getPubkey.c → src/handle_get_pubkey.c
Original file line number Diff line number Diff line change
@@ -1,51 +1,24 @@
#include "apdu.h"
#include "getPubkey.h"
#include "os.h"
#include "ux.h"
#include "utils.h"
#include "globals.h"
#include "handle_get_pubkey.h"
#include "sol/printer.h"
#include "ui_api.h"

static uint8_t G_publicKey[PUBKEY_LENGTH];
static char G_publicKeyStr[BASE58_PUBKEY_LENGTH];
char G_publicKeyStr[BASE58_PUBKEY_LENGTH];

void reset_getpubkey_globals(void) {
MEMCLEAR(G_publicKey);
MEMCLEAR(G_publicKeyStr);
}

static uint8_t set_result_get_pubkey() {
uint8_t set_result_get_pubkey(void) {
memcpy(G_io_apdu_buffer, G_publicKey, PUBKEY_LENGTH);
return PUBKEY_LENGTH;
}

//////////////////////////////////////////////////////////////////////

UX_STEP_NOCB(ux_display_public_flow_5_step,
bnnn_paging,
{
.title = "Pubkey",
.text = G_publicKeyStr,
});
UX_STEP_CB(ux_display_public_flow_6_step,
pb,
sendResponse(set_result_get_pubkey(), true, true),
{
&C_icon_validate_14,
"Approve",
});
UX_STEP_CB(ux_display_public_flow_7_step,
pb,
sendResponse(0, false, true),
{
&C_icon_crossmark,
"Reject",
});

UX_FLOW(ux_display_public_flow,
&ux_display_public_flow_5_step,
&ux_display_public_flow_6_step,
&ux_display_public_flow_7_step);

void handle_get_pubkey(volatile unsigned int *flags, volatile unsigned int *tx) {
if (!flags || !tx ||
(G_command.instruction != InsDeprecatedGetPubkey &&
@@ -61,7 +34,7 @@ void handle_get_pubkey(volatile unsigned int *flags, volatile unsigned int *tx)
*tx = set_result_get_pubkey();
THROW(ApduReplySuccess);
} else {
ux_flow_init(0, ux_display_public_flow, NULL);
ui_get_public_key();
*flags |= IO_ASYNCH_REPLY;
}
}
8 changes: 5 additions & 3 deletions src/getPubkey.h → src/handle_get_pubkey.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#pragma once

#include "os.h"
#include "cx.h"
#include "globals.h"
#include "sol/printer.h"

#ifndef _GET_PUBKEY_H_
#define _GET_PUBKEY_H_
extern char G_publicKeyStr[BASE58_PUBKEY_LENGTH];

void reset_getpubkey_globals(void);

void handle_get_pubkey(volatile unsigned int *flags, volatile unsigned int *tx);

#endif
uint8_t set_result_get_pubkey(void);
98 changes: 6 additions & 92 deletions src/signMessage.c → src/handle_sign_message.c
Original file line number Diff line number Diff line change
@@ -1,90 +1,15 @@
#include "getPubkey.h"
#include "os.h"
#include "ux.h"
#include "cx.h"
#include "menu.h"
#include "io.h"
#include "utils.h"
#include "handle_swap_sign_transaction.h"

#include "sol/parser.h"
#include "sol/printer.h"
#include "sol/print_config.h"
#include "sol/message.h"
#include "sol/transaction_summary.h"
#include "globals.h"
#include "apdu.h"

#include "handle_swap_sign_transaction.h"

static uint8_t set_result_sign_message() {
uint8_t signature[SIGNATURE_LENGTH];
cx_ecfp_private_key_t privateKey;
BEGIN_TRY {
TRY {
get_private_key_with_seed(&privateKey,
G_command.derivation_path,
G_command.derivation_path_length);
cx_eddsa_sign(&privateKey,
CX_LAST,
CX_SHA512,
G_command.message,
G_command.message_length,
NULL,
0,
signature,
SIGNATURE_LENGTH,
NULL);
memcpy(G_io_apdu_buffer, signature, SIGNATURE_LENGTH);
}
CATCH_OTHER(e) {
MEMCLEAR(privateKey);
THROW(e);
}
FINALLY {
MEMCLEAR(privateKey);
}
}
END_TRY;
return SIGNATURE_LENGTH;
}

//////////////////////////////////////////////////////////////////////

UX_STEP_CB(ux_approve_step,
pb,
sendResponse(set_result_sign_message(), true, true),
{
&C_icon_validate_14,
"Approve",
});
UX_STEP_CB(ux_reject_step,
pb,
sendResponse(0, false, true),
{
&C_icon_crossmark,
"Reject",
});
UX_STEP_NOCB_INIT(ux_summary_step,
bnnn_paging,
{
size_t step_index = G_ux.flow_stack[stack_slot].index;
enum DisplayFlags flags = DisplayFlagNone;
if (N_storage.settings.pubkey_display == PubkeyDisplayLong) {
flags |= DisplayFlagLongPubkeys;
}
if (transaction_summary_display_item(step_index, flags)) {
THROW(ApduReplySolanaSummaryUpdateFailed);
}
},
{
.title = G_transaction_summary_title,
.text = G_transaction_summary_text,
});

#define MAX_FLOW_STEPS \
(MAX_TRANSACTION_SUMMARY_ITEMS + 1 /* approve */ \
+ 1 /* reject */ \
+ 1 /* FLOW_END_STEP */ \
)
ux_flow_step_t const *flow_steps[MAX_FLOW_STEPS];
#include "handle_sign_message.h"
#include "ui_api.h"

static int scan_header_for_signer(const uint32_t *derivation_path,
uint32_t derivation_path_length,
@@ -221,18 +146,7 @@ void handle_sign_message_ui(volatile unsigned int *flags) {
THROW(ApduReplySolanaSummaryFinalizeFailed);
}
} else {
MEMCLEAR(flow_steps);
size_t num_flow_steps = 0;

for (size_t i = 0; i < num_summary_steps; i++) {
flow_steps[num_flow_steps++] = &ux_summary_step;
}

flow_steps[num_flow_steps++] = &ux_approve_step;
flow_steps[num_flow_steps++] = &ux_reject_step;
flow_steps[num_flow_steps++] = FLOW_END_STEP;

ux_flow_init(0, flow_steps, NULL);
start_sign_tx_ui(num_summary_steps);
}
} else {
THROW(ApduReplySolanaSummaryFinalizeFailed);
9 changes: 1 addition & 8 deletions src/signMessage.h → src/handle_sign_message.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
#include "os.h"
#include "cx.h"
#include "globals.h"

#ifndef _SIGN_MESSAGE_H_
#define _SIGN_MESSAGE_H_
#pragma once

void handle_sign_message_parse_message(volatile unsigned int *tx);

void handle_sign_message_ui(volatile unsigned int *flags);

#endif
107 changes: 5 additions & 102 deletions src/signOffchainMessage.c → src/handle_sign_offchain_message.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#include "getPubkey.h"
#include "io.h"
#include "os.h"
#include "ux.h"
#include "cx.h"
#include "menu.h"
#include "utils.h"
#include "sol/parser.h"
#include "sol/printer.h"
@@ -11,6 +10,8 @@
#include "sol/transaction_summary.h"
#include "globals.h"
#include "apdu.h"
#include "handle_sign_offchain_message.h"
#include "ui_api.h"

// Store locally the derived public key content
static Pubkey G_publicKey;
@@ -19,7 +20,7 @@ static Pubkey G_publicKey;
* Checks if data is in UTF-8 format.
* Adapted from: https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
*/
bool is_data_utf8(const uint8_t *data, size_t length) {
static bool is_data_utf8(const uint8_t *data, size_t length) {
if (!data) {
return false;
}
@@ -79,95 +80,8 @@ static bool is_data_ascii(const uint8_t *data, size_t length) {
return true;
}

static uint8_t set_result_sign_message() {
uint8_t signature[SIGNATURE_LENGTH];
cx_ecfp_private_key_t privateKey;
BEGIN_TRY {
TRY {
get_private_key_with_seed(&privateKey,
G_command.derivation_path,
G_command.derivation_path_length);
cx_eddsa_sign(&privateKey,
CX_LAST,
CX_SHA512,
G_command.message,
G_command.message_length,
NULL,
0,
signature,
SIGNATURE_LENGTH,
NULL);
memcpy(G_io_apdu_buffer, signature, SIGNATURE_LENGTH);
}
CATCH_OTHER(e) {
MEMCLEAR(privateKey);
THROW(e);
}
FINALLY {
MEMCLEAR(privateKey);
}
}
END_TRY;
return SIGNATURE_LENGTH;
}

//////////////////////////////////////////////////////////////////////

UX_STEP_NOCB(ux_sign_msg_text_step,
bnnn_paging,
{
.title = "Message",
.text = (const char *) G_command.message + OFFCHAIN_MESSAGE_HEADER_LENGTH,
});
UX_STEP_CB(ux_sign_msg_approve_step,
pb,
sendResponse(set_result_sign_message(), true, true),
{
&C_icon_validate_14,
"Approve",
});
UX_STEP_CB(ux_sign_msg_reject_step,
pb,
sendResponse(0, false, true),
{
&C_icon_crossmark,
"Reject",
});
UX_STEP_NOCB_INIT(ux_sign_msg_summary_step,
bnnn_paging,
{
size_t step_index = G_ux.flow_stack[stack_slot].index;
enum DisplayFlags flags = DisplayFlagNone;
if (N_storage.settings.pubkey_display == PubkeyDisplayLong) {
flags |= DisplayFlagLongPubkeys;
}
if (transaction_summary_display_item(step_index, flags)) {
THROW(ApduReplySolanaSummaryUpdateFailed);
}
},
{
.title = G_transaction_summary_title,
.text = G_transaction_summary_text,
});

/*
UX Steps:
- Sign Message

if expert mode:
- Version
- Format
- Size
- Hash
- Signer
else if utf8:
- Hash

if ascii:
- message text
*/
static ux_flow_step_t const *flow_steps[8];

void handle_sign_offchain_message(volatile unsigned int *flags, volatile unsigned int *tx) {
if (!tx || G_command.instruction != InsSignOffchainMessage ||
G_command.state != ApduStatePayloadComplete) {
@@ -233,23 +147,12 @@ void handle_sign_offchain_message(volatile unsigned int *flags, volatile unsigne
}

enum SummaryItemKind summary_step_kinds[MAX_TRANSACTION_SUMMARY_ITEMS];
size_t num_flow_steps = 0;
size_t num_summary_steps = 0;
if (transaction_summary_finalize(summary_step_kinds, &num_summary_steps)) {
THROW(ApduReplySolanaSummaryFinalizeFailed);
}
for (size_t i = 0; i < num_summary_steps; i++) {
flow_steps[num_flow_steps++] = &ux_sign_msg_summary_step;
}

if (is_ascii) {
flow_steps[num_flow_steps++] = &ux_sign_msg_text_step;
}
flow_steps[num_flow_steps++] = &ux_sign_msg_approve_step;
flow_steps[num_flow_steps++] = &ux_sign_msg_reject_step;
flow_steps[num_flow_steps++] = FLOW_END_STEP;

ux_flow_init(0, flow_steps, NULL);
start_sign_offchain_message_ui(is_ascii, num_summary_steps);

*flags |= IO_ASYNCH_REPLY;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
#pragma once

#include "os.h"
#include "cx.h"
#include "globals.h"

#ifndef _SIGN_OFFCHAIN_MESSAGE_H_
#define _SIGN_OFFCHAIN_MESSAGE_H_

void handle_sign_offchain_message(volatile unsigned int *flags, volatile unsigned int *tx);

#endif
100 changes: 100 additions & 0 deletions src/io.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*****************************************************************************
* Ledger App Solana.
* (c) 2023 Ledger SAS.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/

#include <stdint.h>
#include <string.h>

#include "os.h"
#include "ux.h"

#include "apdu.h"
#include "ui_api.h"
#include "io.h"

// override point, but nothing more to do
void io_seproxyhal_display(const bagl_element_t *element) {
io_seproxyhal_display_default(element);
}

uint8_t io_event(uint8_t channel) {
(void) channel;

switch (G_io_seproxyhal_spi_buffer[0]) {
case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT:
UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer);
break;
case SEPROXYHAL_TAG_STATUS_EVENT:
if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID && //
!(U4BE(G_io_seproxyhal_spi_buffer, 3) & //
SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) {
THROW(ApduReplySdkExceptionIoReset);
}
/* fallthrough */
case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT:
UX_DISPLAYED_EVENT({});
break;
case SEPROXYHAL_TAG_TICKER_EVENT:
UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, {});
break;
default:
UX_DEFAULT_EVENT();
break;
}

if (!io_seproxyhal_spi_is_status_sent()) {
io_seproxyhal_general_status();
}

return 1;
}

uint16_t io_exchange_al(uint8_t channel, uint16_t tx_len) {
switch (channel & ~(IO_FLAGS)) {
case CHANNEL_KEYBOARD:
break;

// multiplexed io exchange over a SPI channel and
// TLV encapsulated protocol
case CHANNEL_SPI:
if (tx_len) {
io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len);

if (channel & IO_RESET_AFTER_REPLIED) {
reset();
}
return 0; // nothing received from the master so far
// (it's a tx transaction)
} else {
return io_seproxyhal_spi_recv(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), 0);
}

default:
THROW(ApduReplySdkInvalidParameter);
}
return 0;
}

void sendResponse(uint8_t tx, bool approve, bool display_menu) {
G_io_apdu_buffer[tx++] = approve ? 0x90 : 0x69;
G_io_apdu_buffer[tx++] = approve ? 0x00 : 0x85;
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
if (display_menu) {
// Display back the original UX
ui_idle();
}
}
21 changes: 21 additions & 0 deletions src/io.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <stdint.h>

#include "ux.h"
#include "os_io_seproxyhal.h"

void io_seproxyhal_display(const bagl_element_t *element);

/**
* IO callback called when an interrupt based channel has received
* data to be processed.
*
* @return 1 if success, 0 otherwise.
*
*/
uint8_t io_event(uint8_t channel);

uint16_t io_exchange_al(uint8_t channel, uint16_t tx_len);

void sendResponse(uint8_t tx, bool approve, bool display_menu);
95 changes: 4 additions & 91 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -16,11 +16,11 @@
********************************************************************************/

#include "utils.h"
#include "getPubkey.h"
#include "signMessage.h"
#include "signOffchainMessage.h"
#include "handle_get_pubkey.h"
#include "handle_sign_message.h"
#include "handle_sign_offchain_message.h"
#include "apdu.h"
#include "menu.h"
#include "ui_api.h"

// Swap feature
#include "swap_lib_calls.h"
@@ -162,93 +162,6 @@ void app_main(void) {
}
}

// override point, but nothing more to do
void io_seproxyhal_display(const bagl_element_t *element) {
io_seproxyhal_display_default((bagl_element_t *) element);
}

unsigned char io_event(unsigned char channel) {
UNUSED(channel);

// nothing done with the event, throw an error on the transport layer if
// needed

// can't have more than one tag in the reply, not supported yet.
switch (G_io_seproxyhal_spi_buffer[0]) {
case SEPROXYHAL_TAG_FINGER_EVENT:
UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer);
break;

case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT:
UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer);
break;

case SEPROXYHAL_TAG_STATUS_EVENT:
if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID &&
!(U4BE(G_io_seproxyhal_spi_buffer, 3) &
SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) {
THROW(ApduReplySdkExceptionIoReset);
}
// no break is intentional
default:
UX_DEFAULT_EVENT();
break;

case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT:
UX_DISPLAYED_EVENT({});
break;

case SEPROXYHAL_TAG_TICKER_EVENT:
UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, {
#if !defined(TARGET_NANOX) && !defined(TARGET_NANOS2)
if (UX_ALLOWED) {
if (ux_step_count) {
// prepare next screen
ux_step = (ux_step + 1) % ux_step_count;
// redisplay screen
UX_REDISPLAY();
}
}
#endif // TARGET_NANOX
});
break;
}

// close the event if not done previously (by a display or whatever)
if (!io_seproxyhal_spi_is_status_sent()) {
io_seproxyhal_general_status();
}

// command has been processed, DO NOT reset the current APDU transport
return 1;
}

unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) {
switch (channel & ~(IO_FLAGS)) {
case CHANNEL_KEYBOARD:
break;

// multiplexed io exchange over a SPI channel and
// TLV encapsulated protocol
case CHANNEL_SPI:
if (tx_len) {
io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len);

if (channel & IO_RESET_AFTER_REPLIED) {
reset();
}
return 0; // nothing received from the master so far
// (it's a tx transaction)
} else {
return io_seproxyhal_spi_recv(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), 0);
}

default:
THROW(ApduReplySdkInvalidParameter);
}
return 0;
}

void app_exit(void) {
BEGIN_TRY_L(exit) {
TRY_L(exit) {
9 changes: 0 additions & 9 deletions src/menu.h

This file was deleted.

55 changes: 55 additions & 0 deletions src/ui/get_pubkey_bagl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

/*****************************************************************************
* Ledger App Solana
* (c) 2023 Ledger SAS.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/

#ifdef HAVE_BAGL

#include "handle_get_pubkey.h"
#include "io.h"
#include "ux.h"

UX_STEP_NOCB(ux_display_public_flow_5_step,
bnnn_paging,
{
.title = "Pubkey",
.text = G_publicKeyStr,
});
UX_STEP_CB(ux_display_public_flow_6_step,
pb,
sendResponse(set_result_get_pubkey(), true, true),
{
&C_icon_validate_14,
"Approve",
});
UX_STEP_CB(ux_display_public_flow_7_step,
pb,
sendResponse(0, false, true),
{
&C_icon_crossmark,
"Reject",
});

UX_FLOW(ux_display_public_flow,
&ux_display_public_flow_5_step,
&ux_display_public_flow_6_step,
&ux_display_public_flow_7_step);

void ui_get_public_key(void) {
ux_flow_init(0, ux_display_public_flow, NULL);
}

#endif
30 changes: 20 additions & 10 deletions src/menu.c → src/ui/menu_bagl.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
#include "menu.h"
#ifdef HAVE_BAGL

#include "ux.h"
#include "os.h"
#include "globals.h"
#include "glyphs.h"
#include "ui_api.h"

void display_settings(void);
void switch_allow_blind_sign_data(void);
void switch_pubkey_display_data(void);

//////////////////////////////////////////////////////////////////////
const char* settings_submenu_getter(unsigned int idx);
void settings_submenu_selector(unsigned int idx);
static const char* settings_submenu_getter(unsigned int idx);
static void settings_submenu_selector(unsigned int idx);
static const char* allow_blind_sign_data_getter(unsigned int idx);
static void allow_blind_sign_data_selector(unsigned int idx);
static const char* pubkey_display_data_getter(unsigned int idx);
@@ -26,7 +31,7 @@ enum SettingsMenuOption {
SettingsMenuOptionBack
};

unsigned int settings_submenu_option_index(enum SettingsMenuOption settings_menu_option) {
static unsigned int settings_submenu_option_index(enum SettingsMenuOption settings_menu_option) {
switch (settings_menu_option) {
case SettingsMenuOptionAllowBlindSign:
case SettingsMenuOptionPubkeyLength:
@@ -45,14 +50,14 @@ const char* const settings_submenu_getter_values[] = {
"Back",
};

const char* settings_submenu_getter(unsigned int idx) {
static const char* settings_submenu_getter(unsigned int idx) {
if (idx < ARRAYLEN(settings_submenu_getter_values)) {
return settings_submenu_getter_values[idx];
}
return NULL;
}

void settings_submenu_selector(unsigned int idx) {
static void settings_submenu_selector(unsigned int idx) {
switch (idx) {
case 0:
ux_menulist_init_select(0,
@@ -100,7 +105,7 @@ static const char* allow_blind_sign_data_getter(unsigned int idx) {
return NULL;
}

void allow_blind_sign_data_selector(unsigned int idx) {
static void allow_blind_sign_data_selector(unsigned int idx) {
switch (idx) {
case 0:
allow_blind_sign_data_change(BlindSignDisabled);
@@ -118,7 +123,7 @@ void allow_blind_sign_data_selector(unsigned int idx) {
//////////////////////////////////////////////////////////////////////////////////////
// Pubkey display submenu

void pubkey_display_data_change(enum PubkeyDisplay pubkey_display) {
static void pubkey_display_data_change(enum PubkeyDisplay pubkey_display) {
uint8_t value;
switch (pubkey_display) {
case PubkeyDisplayLong:
@@ -156,7 +161,7 @@ static void pubkey_display_data_selector(unsigned int idx) {
//////////////////////////////////////////////////////////////////////////////////////
// Display mode submenu

void display_mode_data_change(enum DisplayMode display_mode) {
static void display_mode_data_change(enum DisplayMode display_mode) {
uint8_t value;
switch (display_mode) {
case DisplayModeUser:
@@ -192,10 +197,11 @@ static void display_mode_data_selector(unsigned int idx) {
}

//////////////////////////////////////////////////////////////////////

UX_STEP_NOCB(ux_idle_flow_1_step,
pnn,
{
&C_solana_logo,
&C_icon_solana_16x16,
"Application",
"is ready",
});
@@ -226,10 +232,14 @@ UX_FLOW(ux_idle_flow,
&ux_idle_flow_4_step,
FLOW_LOOP);

//////////////////////////////////////////////////////////////////////

void ui_idle(void) {
// reserve a display stack slot if none yet
if (G_ux.stack_count == 0) {
ux_stack_push();
}
ux_flow_init(0, ux_idle_flow, NULL);
}

#endif
116 changes: 116 additions & 0 deletions src/ui/sign_message_bagl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#ifdef HAVE_BAGL

#include "io.h"
#include "utils.h"
#include "sol/parser.h"
#include "sol/printer.h"
#include "sol/print_config.h"
#include "sol/message.h"
#include "sol/transaction_summary.h"
#include "apdu.h"

#include "handle_sign_message.h"

// Display offchain message screen
UX_STEP_NOCB(ux_sign_msg_text_step,
bnnn_paging,
{
.title = "Message",
.text = (const char *) G_command.message + OFFCHAIN_MESSAGE_HEADER_LENGTH,
});

// Display dynamic transaction item screen
UX_STEP_NOCB_INIT(ux_summary_step,
bnnn_paging,
{
size_t step_index = G_ux.flow_stack[stack_slot].index;
enum DisplayFlags flags = DisplayFlagNone;
if (N_storage.settings.pubkey_display == PubkeyDisplayLong) {
flags |= DisplayFlagLongPubkeys;
}
if (transaction_summary_display_item(step_index, flags)) {
THROW(ApduReplySolanaSummaryUpdateFailed);
}
},
{
.title = G_transaction_summary_title,
.text = G_transaction_summary_text,
});

// Approve and sign screen
UX_STEP_CB(ux_approve_step,
pb,
sendResponse(set_result_sign_message(), true, true),
{
&C_icon_validate_14,
"Approve",
});

// Reject signature screen
UX_STEP_CB(ux_reject_step,
pb,
sendResponse(0, false, true),
{
&C_icon_crossmark,
"Reject",
});

#define MAX_FLOW_STEPS_ONCHAIN \
(MAX_TRANSACTION_SUMMARY_ITEMS + 1 /* approve */ \
+ 1 /* reject */ \
+ 1 /* FLOW_END_STEP */ \
)
/*
OFFCHAIN UX Steps:
- Sign Message
if expert mode:
- Version
- Format
- Size
- Hash
- Signer
else if utf8:
- Hash
if ascii:
- message text
*/
#define MAX_FLOW_STEPS_OFFCHAIN \
(7 + 1 /* approve */ \
+ 1 /* reject */ \
+ 1 /* FLOW_END_STEP */ \
)
static ux_flow_step_t const *flow_steps[MAX(MAX_FLOW_STEPS_ONCHAIN, MAX_FLOW_STEPS_OFFCHAIN)];

void start_sign_tx_ui(size_t num_summary_steps) {
MEMCLEAR(flow_steps);
size_t num_flow_steps = 0;
for (size_t i = 0; i < num_summary_steps; i++) {
flow_steps[num_flow_steps++] = &ux_summary_step;
}

flow_steps[num_flow_steps++] = &ux_approve_step;
flow_steps[num_flow_steps++] = &ux_reject_step;
flow_steps[num_flow_steps++] = FLOW_END_STEP;

ux_flow_init(0, flow_steps, NULL);
}

void start_sign_offchain_message_ui(bool is_ascii, size_t num_summary_steps) {
MEMCLEAR(flow_steps);
size_t num_flow_steps = 0;
for (size_t i = 0; i < num_summary_steps; i++) {
flow_steps[num_flow_steps++] = &ux_summary_step;
}
if (is_ascii) {
flow_steps[num_flow_steps++] = &ux_sign_msg_text_step;
}
flow_steps[num_flow_steps++] = &ux_approve_step;
flow_steps[num_flow_steps++] = &ux_reject_step;
flow_steps[num_flow_steps++] = FLOW_END_STEP;

ux_flow_init(0, flow_steps, NULL);
}

#endif
11 changes: 11 additions & 0 deletions src/ui/ui_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once

#include "os.h"

void ui_idle(void);

void ui_get_public_key(void);

void start_sign_tx_ui(size_t num_summary_steps);

void start_sign_offchain_message_ui(bool is_ascii, size_t num_summary_steps);
51 changes: 27 additions & 24 deletions src/utils.c
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@
#include <stdbool.h>
#include <stdlib.h>
#include "utils.h"
#include "menu.h"

void get_public_key(uint8_t *publicKeyArray, const uint32_t *derivationPath, size_t pathLength) {
cx_ecfp_private_key_t privateKey;
@@ -126,29 +125,33 @@ int read_derivation_path(const uint8_t *data_buffer,
return 0;
}

void sendResponse(uint8_t tx, bool approve, bool display_menu) {
G_io_apdu_buffer[tx++] = approve ? 0x90 : 0x69;
G_io_apdu_buffer[tx++] = approve ? 0x00 : 0x85;
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
if (display_menu) {
// Display back the original UX
ui_idle();
}
}

unsigned int ui_prepro(const bagl_element_t *element) {
unsigned int display = 1;
if (element->component.userid > 0) {
display = (ux_step == element->component.userid - 1);
if (display) {
if (element->component.userid == 1) {
UX_CALLBACK_SET_INTERVAL(2000);
} else {
UX_CALLBACK_SET_INTERVAL(
MAX(3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
}
uint8_t set_result_sign_message(void) {
uint8_t signature[SIGNATURE_LENGTH];
cx_ecfp_private_key_t privateKey;
BEGIN_TRY {
TRY {
get_private_key_with_seed(&privateKey,
G_command.derivation_path,
G_command.derivation_path_length);
cx_eddsa_sign(&privateKey,
CX_LAST,
CX_SHA512,
G_command.message,
G_command.message_length,
NULL,
0,
signature,
SIGNATURE_LENGTH,
NULL);
memcpy(G_io_apdu_buffer, signature, SIGNATURE_LENGTH);
}
CATCH_OTHER(e) {
THROW(e);
}
FINALLY {
MEMCLEAR(privateKey);
}
}
return display;
END_TRY;
return SIGNATURE_LENGTH;
}
54 changes: 5 additions & 49 deletions src/utils.h
Original file line number Diff line number Diff line change
@@ -7,6 +7,8 @@
#ifndef _UTILS_H_
#define _UTILS_H_

#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))

// Marker flag for DEPRECATED ADPU exchange format
#define DATA_HAS_LENGTH_PREFIX (1 << 15)

@@ -19,8 +21,6 @@ typedef enum rlpTxType {
TX_FEE
} rlpTxType;

unsigned int ui_prepro(const bagl_element_t *element);

void get_public_key(uint8_t *publicKeyArray, const uint32_t *derivationPath, size_t pathLength);

uint32_t readUint32BE(uint8_t *buffer);
@@ -53,55 +53,11 @@ int read_derivation_path(const uint8_t *data_buffer,
uint32_t *derivation_path,
uint32_t *derivation_path_length);

void sendResponse(uint8_t tx, bool approve, bool display_menu);

// type userid x y w h str rad fill fg bg fid iid txt
// touchparams... ]
#define UI_BUTTONS \
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, \
NULL, \
0, \
0, \
0, \
NULL, \
NULL, \
NULL}, \
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS}, \
NULL, \
0, \
0, \
0, \
NULL, \
NULL, \
NULL}, \
{ \
{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK}, \
NULL, 0, 0, 0, NULL, NULL, NULL \
}

#define UI_FIRST 1
#define UI_SECOND 0

#define UI_LABELINE(userId, text, isFirst, font, horizontalScrollSpeed) \
{ \
{BAGL_LABELINE, \
(userId), \
23, \
(isFirst) ? 12 : 26, \
82, \
12, \
(horizontalScrollSpeed) ? BAGL_STROKE_FLAG_ONESHOT | 10 : 0, \
0, \
0, \
0xFFFFFF, \
0x000000, \
(font) | BAGL_FONT_ALIGNMENT_CENTER, \
horizontalScrollSpeed}, \
(text), 0, 0, 0, NULL, NULL, NULL \
}
uint8_t set_result_sign_message(void);

#endif
#endif //_UTILS_H_

// Outdated ?
#ifdef TEST
#include <stdio.h>
#define THROW(code) \