Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ tests/__pycache__/

# IDE settings
.vscode/
.venv
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ VARIANT_VALUES=$(COIN)
GIT_DESCRIBE=$(shell git describe --tags --abbrev=8 --always --long --dirty 2>/dev/null)
VERSION_TAG=$(shell echo $(GIT_DESCRIBE) | sed 's/^v//g')
APPVERSION_M=1
APPVERSION_N=5
APPVERSION_P=1
APPVERSION_N=6
APPVERSION_P=5
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
APPNAME = "Mina"

Expand Down
18 changes: 10 additions & 8 deletions src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -652,10 +652,10 @@ bool validate_address(const char *address)
return memcmp(raw->checksum, hash2, 4) == 0;
}

bool message_derive(Scalar out, const Keypair *kp, const ROInput *input, const uint8_t network_id)
bool message_derive(Scalar out, const Keypair *kp, const ROInput *input, const uint8_t network_id, poseidon_mode_t mode)
{
uint8_t derive_msg[268] = { };
int derive_len = roinput_derive_message(derive_msg, sizeof(derive_msg), kp, input, network_id);
int derive_len = roinput_derive_message(derive_msg, sizeof(derive_msg), kp, input, network_id, mode);
if (derive_len < 0) {
return false;
}
Expand Down Expand Up @@ -689,7 +689,7 @@ bool message_derive(Scalar out, const Keypair *kp, const ROInput *input, const u
return true;
}

bool message_hash(Scalar out, const Affine *pub, const Field rx, const ROInput *input, const uint8_t network_id)
bool message_hash(Scalar out, const Affine *pub, const Field rx, const ROInput *input, const uint8_t network_id, poseidon_mode_t mode)
{
Field hash_msg[9];
int hash_msg_len = roinput_hash_message(hash_msg, sizeof(hash_msg), pub, rx, input);
Expand All @@ -699,14 +699,16 @@ bool message_hash(Scalar out, const Affine *pub, const Field rx, const ROInput *

// Initial sponge state
State pos;
poseidon_init(pos, network_id);
poseidon_update(pos, hash_msg, hash_msg_len);
if (!poseidon_init(pos, network_id, mode)) {
return false;
}
poseidon_update(pos, hash_msg, hash_msg_len, mode);
poseidon_digest(out, pos);

return true;
}

bool sign(Signature *sig, const Keypair *kp, const ROInput *input, const uint8_t network_id)
bool sign(Signature *sig, const Keypair *kp, const ROInput *input, const uint8_t network_id, poseidon_mode_t mode)
{
Scalar k;
Affine r;
Expand All @@ -716,7 +718,7 @@ bool sign(Signature *sig, const Keypair *kp, const ROInput *input, const uint8_t
BEGIN_TRY {
TRY {
// k = message_derive(input.fields + kp.pub + input.bits + kp.priv)
if (!message_derive(k, kp, input, network_id)) {
if (!message_derive(k, kp, input, network_id, mode)) {
THROW(INVALID_PARAMETER);
}

Expand All @@ -737,7 +739,7 @@ bool sign(Signature *sig, const Keypair *kp, const ROInput *input, const uint8_t
}

// e = message_hash(input + kp.pub + r.x)
if (!message_hash(sig->s, &kp->pub, r.x, input, network_id)) {
if (!message_hash(sig->s, &kp->pub, r.x, input, network_id, mode)) {
THROW(INVALID_PARAMETER);
}

Expand Down
8 changes: 7 additions & 1 deletion src/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ typedef struct keypair_t {

typedef struct roinput_t ROInput; // Forward declaration

// Forward declaration for poseidon mode
typedef enum {
POSEIDON_LEGACY,
POSEIDON_KIMCHI
} poseidon_mode_t;

void field_copy(Field b, const Field a);
void field_add(Field c, const Field a, const Field b);
void field_mul(Field c, const Field a, const Field b);
Expand All @@ -81,4 +87,4 @@ void generate_pubkey(Affine *pub_key, const Scalar priv_key);
bool generate_address(char *address, const size_t len, const Affine *pub_key);
bool validate_address(const char *address);

bool sign(Signature *sig, const Keypair *kp, const ROInput *input, const uint8_t network_id);
bool sign(Signature *sig, const Keypair *kp, const ROInput *input, const uint8_t network_id, poseidon_mode_t mode);
13 changes: 10 additions & 3 deletions src/get_address.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#include "get_address.h"
#include "utils.h"
#include "crypto.h"
#include "menu.h"

static uint32_t _account = 0;
char _bip44_path[27]; // max length when 44'/12586'/4294967295'/0/0
char _address[MINA_ADDRESS_LEN];


static void compute_address_and_response() {
show_processing();
compute_address();
sendResponse(set_result_get_address(), true);
ui_idle();
}

void handle_get_address(uint8_t p1, uint8_t p2, uint8_t *dataBuffer,
Expand Down Expand Up @@ -64,12 +67,16 @@ uint8_t set_result_get_address()
return tx;
}

void compute_address() {
_address[0] = '\0';

void prepare_bip44_path() {
strncpy(_bip44_path, "44'/12586'/", sizeof(_bip44_path)); // used 11/27 (not counting null-byte)
value_to_string(&_bip44_path[11], sizeof(_bip44_path) - 11, _account); // at most 21/27 used (max strnlen is 10 when _account = 4294967295)
strncat(_bip44_path, "'/0/0", 6); // at least 27 - 21 = 6 bytes free (just enough)
}

void compute_address() {
_address[0] = '\0';

prepare_bip44_path();

gen_address(_account, _address);
}
3 changes: 3 additions & 0 deletions src/get_address.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

uint8_t set_result_get_address();

void prepare_bip44_path();

void compute_address();

void show_address_and_response();
void show_processing();

void handle_get_address(uint8_t p1, uint8_t p2, uint8_t *dataBuffer,
uint8_t dataLength, volatile unsigned int *flags);
Expand Down
27 changes: 11 additions & 16 deletions src/get_address_bagl.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,19 @@ extern char _address[MINA_ADDRESS_LEN];
&ux_get_address_result_flow_reject_step
);

#ifndef HAVE_CRYPTO_TESTS
UX_STEP_TIMEOUT(
ux_get_address_comfort_flow_processing_step,
UX_STEP_NOCB(
ux_get_address_processing_step,
pb,
1,
ux_get_address_result_flow,
{
&C_icon_processing,
"Processing...",
}
);

UX_FLOW(
ux_get_address_comfort_flow,
&ux_get_address_comfort_flow_processing_step
ux_get_address_processing_flow,
&ux_get_address_processing_step
);
#endif

UX_STEP_NOCB(
ux_get_address_flow_topic_step,
Expand All @@ -110,15 +106,14 @@ extern char _address[MINA_ADDRESS_LEN];
}
);

void show_processing() {
ux_flow_init(0, ux_get_address_processing_flow, NULL);
}

UX_STEP_VALID(
ux_get_address_flow_generate_step,
pb,
#ifndef HAVE_CRYPTO_TESTS
ux_flow_init(0, ux_get_address_comfort_flow, NULL);,

#else
ux_flow_init(0, ux_get_address_result_flow, NULL);,
#endif
{ show_processing(); compute_address(); ux_flow_init(0, ux_get_address_result_flow, NULL); },
{
&C_icon_validate_14,
"Generate"
Expand All @@ -145,11 +140,11 @@ extern char _address[MINA_ADDRESS_LEN];
#endif

void show_address_and_response() {
compute_address();

#ifdef HAVE_ON_DEVICE_UNIT_TESTS
compute_address();
ux_flow_init(0, ux_get_address_unit_test_flow, NULL);
#else
prepare_bip44_path();
ux_flow_init(0, ux_get_address_flow, NULL);
#endif
}
Expand Down
5 changes: 5 additions & 0 deletions src/get_address_nbgl.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ static void confirmation_callback(bool confirm) {
}
}

void show_processing() {
nbgl_useCaseSpinner("Processing");
}

void show_address_and_response() {
show_processing();
compute_address();

transactionContext.tagValuePair[0].item = "Path";
Expand Down
3 changes: 3 additions & 0 deletions src/globals.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ unsigned int ux_step;
unsigned int ux_step_count;
const internalStorage_t N_storage_real;

bool review_pending = false;

void sendResponse(uint8_t tx, bool approve) {
review_pending = false;
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
Expand Down
8 changes: 8 additions & 0 deletions src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <os.h>
#include <ux.h>
#include <os_io_seproxyhal.h>
#include <stdbool.h>

#define P1_FIRST 0x00
#define P1_MORE 0x80
Expand All @@ -15,11 +16,18 @@ extern unsigned int ux_step_count;

typedef struct internalStorage_t {
uint8_t initialized;
uint8_t blindsign_enabled; // 0x00 = disabled (default), 0x01 = enabled
} internalStorage_t;

extern const internalStorage_t N_storage_real;
#define N_storage (*(volatile internalStorage_t*) PIC(&N_storage_real))

// True while a user-confirmation UI flow is open and the device has
// deferred its APDU reply with IO_ASYNCH_REPLY. Any new APDU arriving
// in this state must be rejected so the host cannot mutate the bytes
// being reviewed.
extern bool review_pending;

void sendResponse(uint8_t tx, bool approve);

// type userid x y w h str rad fill fg bg fid iid txt touchparams... ]
Expand Down
35 changes: 34 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#define INS_SIGN_TX 0x03
#define INS_TEST_CRYPTO 0x04
#define INS_SIGN_MSG 0x05
#define INS_SIGN_FIELD_ELEMENT 0x06

#define APDU_HEADER_LEN 5U
#define OFFSET_CLA 0
Expand All @@ -47,6 +48,20 @@ void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx,
THROW(0x6E00);
}

// Refuse any new command while a user-confirmation screen is
// still waiting for approval. Without this, a second APDU can
// race into handler state (globals, review buffers) during the
// asynchronous review window — exploitable on transports that
// do not gate new APDUs at the SDK layer (BLE).
if (review_pending) {
THROW(0x6985);
}
// An APDU is now in flight. Non-review commands release this
// lock when their response is emitted (sendResponse or the
// THROW handled in app_main's CATCH_OTHER). Review-based
// commands keep it until the user approves/rejects.
review_pending = true;

// Check user supplied command data length against the actual
// length to make sure it's not a lie!
uint8_t dataLength = G_io_apdu_buffer[OFFSET_LC];
Expand Down Expand Up @@ -80,7 +95,14 @@ void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx,
handle_sign_msg(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA,
dataLength, flags);
dataLength, flags, POSEIDON_LEGACY);
break;

case INS_SIGN_FIELD_ELEMENT:
handle_sign_msg(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA,
dataLength, flags, POSEIDON_KIMCHI);
break;

#ifdef HAVE_CRYPTO_TESTS
Expand Down Expand Up @@ -164,6 +186,11 @@ void app_main(void) {
handleApdu(&flags, &tx, rx);
}
CATCH(EXCEPTION_IO_RESET) {
// Transport went away mid-review (BLE disconnect, USB
// replug). The deferred reply will never arrive, so drop
// the lock before we unwind — otherwise the dispatcher
// stays frozen after re-init.
review_pending = false;
THROW(EXCEPTION_IO_RESET);
}
CATCH_OTHER(e) {
Expand All @@ -183,6 +210,11 @@ void app_main(void) {
if (e != APDU_CODE_OK) {
flags &= ~IO_ASYNCH_REPLY;
}
// Any THROW exits the APDU — whether a success code
// (e.g. GET_CONF) or an error. Handlers that genuinely
// own a review return normally and keep the lock; we
// only reach here when the APDU is done.
review_pending = false;
// Unexpected exception => report
G_io_apdu_buffer[tx] = sw >> 8;
G_io_apdu_buffer[tx + 1] = sw;
Expand Down Expand Up @@ -311,6 +343,7 @@ void nv_app_state_init(void) {
if (N_storage.initialized != 0x01) {
internalStorage_t storage;
storage.initialized = 0x01;
storage.blindsign_enabled = 0x00; // Disabled by default
nvm_write((internalStorage_t*)&N_storage, (void*)&storage, sizeof(internalStorage_t));
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
#include "glyphs.h"

void ui_idle(void);

bool is_blindsign_enabled(void);
void toggle_blindsign(void);
20 changes: 20 additions & 0 deletions src/menu_bagl.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
#define PRODUCTION_BUILD 0
#endif

static char blindsign_text[20];

static void update_blindsign_text(void) {
if (is_blindsign_enabled()) {
strcpy(blindsign_text, "Enabled");
} else {
strcpy(blindsign_text, "Disabled");
}
}

#ifdef HAVE_ON_DEVICE_UNIT_TESTS
UX_STEP_NOCB(
ux_idle_flow_1_step,
Expand Down Expand Up @@ -54,6 +64,15 @@ UX_STEP_NOCB(
"Copyright",
"(c) 2024 Ledger",
});
UX_STEP_CB_INIT(
ux_idle_flow_blindsign_step,
bn,
update_blindsign_text(),
toggle_blindsign(); ui_idle();,
{
"Blind signing",
blindsign_text,
});
UX_STEP_VALID(
ux_idle_flow_5_step,
pb,
Expand All @@ -68,6 +87,7 @@ UX_FLOW(ux_idle_flow,
&ux_idle_flow_2_step,
&ux_idle_flow_3_step,
&ux_idle_flow_4_step,
&ux_idle_flow_blindsign_step,
&ux_idle_flow_5_step,
FLOW_LOOP
);
Expand Down
10 changes: 10 additions & 0 deletions src/menu_common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "menu.h"

bool is_blindsign_enabled(void) {
return N_storage.blindsign_enabled == 0x01;
}

void toggle_blindsign(void) {
uint8_t value = N_storage.blindsign_enabled ? 0x00 : 0x01;
nvm_write((void*)&N_storage.blindsign_enabled, &value, sizeof(value));
}
Loading
Loading