Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
37e19e7
Extended signing support for Off-chain message signing
0xMMBD Dec 14, 2023
5331908
Merge fixes
AntekWozniakBlocky Mar 21, 2025
11f8a06
Revoked important fix for showing summary text
AntekWozniakBlocky Mar 21, 2025
028bfc0
Uncommented tests in test_solana
0xMMBD Sep 17, 2023
3d081f3
Added missing SignOffChain packet fields, and regenerated "golden" te…
AntekWozniakBlocky Mar 23, 2025
1c81b50
Updated signing on Ledger Stax
AntekWozniakBlocky Mar 23, 2025
9dca0dc
Updated signing on Ledger Flex
AntekWozniakBlocky Mar 23, 2025
792bbd7
Add missing function declarations in header files to resolve warnings
AntekWozniakBlocky Mar 24, 2025
a51f3a4
Fixes for linter and check speller
AntekWozniakBlocky Mar 24, 2025
e9ac0db
Fixed whitespaces for linter
AntekWozniakBlocky Mar 24, 2025
cac73a1
Fixed whitespaces for linter
AntekWozniakBlocky Mar 24, 2025
ce1371a
Removed newline for linter
AntekWozniakBlocky Mar 24, 2025
e6c5b44
Applied clang-format for libsol and src
AntekWozniakBlocky Mar 24, 2025
769b843
Reformated with clang-format version 12.0
AntekWozniakBlocky Mar 24, 2025
1d803c5
Added tests for application name in OCMS
AntekWozniakBlocky Mar 24, 2025
fb5a6c1
Added conditional compilation to support both new and old devices (BG…
AntekWozniakBlocky Mar 24, 2025
bc3237f
Suppressed warning from clang static analyzer
AntekWozniakBlocky Mar 24, 2025
595b233
Update sign_message_nbgl.c to ensure compliance with Ledger guidelines
AntekWozniakBlocky Mar 24, 2025
0ae90fb
Updated file to comply with Ledger standards
AntekWozniakBlocky Mar 24, 2025
c1d6b52
Forget to place closing brace
AntekWozniakBlocky Mar 24, 2025
abe02a9
Regenerated golden run for trusted name creation
AntekWozniakBlocky Mar 24, 2025
0268176
Review changes
AntekWozniakBlocky Mar 27, 2025
eafb75e
Review changes
AntekWozniakBlocky Mar 27, 2025
7908f8a
Removed unnecessary old code
AntekWozniakBlocky Mar 27, 2025
556cb54
Review changes
AntekWozniakBlocky Mar 27, 2025
5d5d4aa
Removed blank lines
AntekWozniakBlocky Mar 27, 2025
1ce1615
Added test for too long message
AntekWozniakBlocky Mar 27, 2025
a643d98
Changed max message length from 1200 B to 14336 B
AntekWozniakBlocky Mar 27, 2025
056203f
Changes to satisfy linter and spell checker
AntekWozniakBlocky Mar 27, 2025
dd38637
Added a file with random ASCII as an exception when checking for miss…
AntekWozniakBlocky Mar 27, 2025
e0c7884
chore: remove unnecessary change
0xMMBD Mar 31, 2025
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
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/lint-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ jobs:
# Use Config file when the github action supports it
builtin: clear,rare
check_filenames: true
skip: ./libsol/printer_test.c,./tests/Cargo.lock,./tools/apdu_generator/Cargo.lock
skip: ./libsol/printer_test.c,./tests/Cargo.lock,./tools/apdu_generator/Cargo.lock,./tests/python/apps/solana_utils.py
4 changes: 4 additions & 0 deletions libsol/common_byte_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,7 @@
0x06, 0xa7, 0xd5, 0x17, 0x19, 0x2c, 0x5c, 0x51, 0x21, 0x8c, 0xc9, 0x4c, 0x3d, 0x4a, 0xf1, \
0x7f, 0x58, 0xda, 0xee, 0x08, 0x9b, 0xa1, 0xfd, 0x44, 0xe3, 0xdb, 0xd9, 0x8a, 0x00, 0x00, \
0x00, 0x00

// Domain specifiers
#define OFFCHAIN_MESSAGE_SIGNING_DOMAIN /* "\xffsolana offchain" */ \
0xff, 0x73, 0x6f, 0x6c, 0x61, 0x6e, 0x61, 0x20, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e
19 changes: 19 additions & 0 deletions libsol/include/sol/offchain_message_signing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once
#include <stdint.h>

#define OFFCHAIN_MESSAGE_SIGNING_DOMAIN_LENGTH 16

/**
* 1. Signing domain (16 bytes)
* 2. Header version (1 byte)
* 3. Application domain (32 bytes)
* 4. Message format (1 byte)
* 5. Signer count (1 bytes)
* 6. Signers (signer_count * 32 bytes) - assume that only one signer is present
* 7. Message length (2 bytes)
*/
typedef struct OffchainMessageSigningDomain {
uint8_t data[OFFCHAIN_MESSAGE_SIGNING_DOMAIN_LENGTH];
} OffchainMessageSigningDomain;

extern const OffchainMessageSigningDomain offchain_message_signing_domain;
9 changes: 9 additions & 0 deletions libsol/include/sol/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,18 @@ typedef struct MessageHeader {
size_t instructions_length;
} MessageHeader;

//@TODO move to offchain message sign .h
#define OFFCHAIN_MESSAGE_APPLICATION_DOMAIN_LENGTH 32
typedef struct OffchainMessageApplicationDomain {
uint8_t data[OFFCHAIN_MESSAGE_APPLICATION_DOMAIN_LENGTH];
} OffchainMessageApplicationDomain;

typedef struct OffchainMessageHeader {
uint8_t version;
const OffchainMessageApplicationDomain *application_domain;
uint8_t format;
size_t signers_length;
const Pubkey *signers;
uint16_t length;
} OffchainMessageHeader;

Expand Down
7 changes: 7 additions & 0 deletions libsol/include/sol/string_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
#include <string.h>
#include <stdbool.h>

bool is_data_utf8(const uint8_t *data, size_t length);
bool is_data_ascii(const uint8_t *data, size_t length);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That util functions are required for parsing off chain payload

6 changes: 6 additions & 0 deletions libsol/include/sol/transaction_summary.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ enum SummaryItemKind {
SummaryItemSizedString,
SummaryItemString,
SummaryItemTimestamp,
SummaryItemOffchainMessageApplicationDomain,
};
typedef enum SummaryItemKind SummaryItemKind_t;

typedef struct SummaryItem SummaryItem;

extern char G_transaction_summary_title[TITLE_SIZE];
#define TEXT_BUFFER_LENGTH BASE58_PUBKEY_LENGTH

extern char G_transaction_summary_text[TEXT_BUFFER_LENGTH];

void transaction_summary_reset();
Expand Down Expand Up @@ -85,3 +87,7 @@ void summary_item_set_hash(SummaryItem *item, const char *title, const Hash *val
void summary_item_set_sized_string(SummaryItem *item, const char *title, const SizedString *value);
void summary_item_set_string(SummaryItem *item, const char *title, const char *value);
void summary_item_set_timestamp(SummaryItem *item, const char *title, int64_t value);
void summary_item_set_offchain_message_application_domain(
SummaryItem *item,
const char *title,
const OffchainMessageApplicationDomain *value);
2 changes: 1 addition & 1 deletion libsol/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ int process_message_body(const uint8_t *message_body,
// Ensure we've consumed the entire message body
BAIL_IF(!parser_is_empty(&parser));

// If we don't know about all of the instructions, bail
// If we don't know about all the instructions, bail
for (size_t i = 0; i < instruction_count; i++) {
BAIL_IF(instruction_info[i].kind == ProgramIdUnknown);
}
Expand Down
5 changes: 5 additions & 0 deletions libsol/offchain_message_signing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "sol/offchain_message_signing.h"
#include "common_byte_strings.h"

const OffchainMessageSigningDomain offchain_message_signing_domain = {
{OFFCHAIN_MESSAGE_SIGNING_DOMAIN}};
46 changes: 34 additions & 12 deletions libsol/parser.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
#include "sol/parser.h"
#include "sol/offchain_message_signing.h"
#include "util.h"

#define OFFCHAIN_MESSAGE_SIGNING_DOMAIN \
"\xff" \
"solana offchain"

static int check_buffer_length(Parser *parser, size_t num) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved out to common_byte_strings.h

return parser->buffer_length < num ? 1 : 0;
}
Expand Down Expand Up @@ -115,6 +112,14 @@ int parse_pubkeys(Parser *parser, PubkeysHeader *header, const Pubkey **pubkeys)
return 0;
}

int parse_pubkeys_with_len(Parser *parser, size_t num_pubkeys, const Pubkey **pubkeys) {
size_t pubkeys_size = num_pubkeys * PUBKEY_SIZE;
BAIL_IF(check_buffer_length(parser, pubkeys_size));
*pubkeys = (const Pubkey *) parser->buffer;
advance(parser, pubkeys_size);
return 0;
}

int parse_hash(Parser *parser, const Hash **hash) {
BAIL_IF(check_buffer_length(parser, HASH_SIZE));
*hash = (const Hash *) parser->buffer;
Expand All @@ -136,26 +141,43 @@ int parse_version(Parser *parser, MessageHeader *header) {
return 0;
}

int parse_offchain_message_application_domain(Parser *parser,
const OffchainMessageApplicationDomain **app_domain) {
BAIL_IF(check_buffer_length(parser, OFFCHAIN_MESSAGE_APPLICATION_DOMAIN_LENGTH));
*app_domain = (const OffchainMessageApplicationDomain *) parser->buffer;
advance(parser, OFFCHAIN_MESSAGE_APPLICATION_DOMAIN_LENGTH);
return 0;
}

int parse_message_header(Parser *parser, MessageHeader *header) {
BAIL_IF(parse_version(parser, header));
BAIL_IF(parse_pubkeys(parser, &header->pubkeys_header, &header->pubkeys));
BAIL_IF(parse_pubkeys_header(parser, &header->pubkeys_header));
BAIL_IF(
parse_pubkeys_with_len(parser, header->pubkeys_header.pubkeys_length, &header->pubkeys));
BAIL_IF(parse_blockhash(parser, &header->blockhash));
BAIL_IF(parse_length(parser, &header->instructions_length));
return 0;
}

int parse_offchain_message_header(Parser *parser, OffchainMessageHeader *header) {
const size_t domain_len = strlen(OFFCHAIN_MESSAGE_SIGNING_DOMAIN);
const size_t domain_len = OFFCHAIN_MESSAGE_SIGNING_DOMAIN_LENGTH;
BAIL_IF(check_buffer_length(parser, domain_len));
int res;
if ((res = memcmp(OFFCHAIN_MESSAGE_SIGNING_DOMAIN, parser->buffer, domain_len)) != 0) {
if ((res =
memcmp((const void *) &offchain_message_signing_domain, parser->buffer, domain_len)) !=
0) {
return res;
}
advance(parser, domain_len);

BAIL_IF(parse_u8(parser, &header->version));
BAIL_IF(parse_u8(parser, &header->format));
BAIL_IF(parse_u16(parser, &header->length));
advance(parser, domain_len); // Signing domain - 16 bytes

BAIL_IF(parse_u8(parser, &header->version)); // Header version
BAIL_IF(parse_offchain_message_application_domain(parser, &header->application_domain));
BAIL_IF(parse_u8(parser, &header->format)); // Message format
uint8_t signers_length = 0;
BAIL_IF(parse_u8(parser, &signers_length)); // Signer count
header->signers_length = signers_length;
BAIL_IF(parse_pubkeys_with_len(parser, header->signers_length, &header->signers));
BAIL_IF(parse_u16(parser, &header->length)); // Message length
return 0;
}

Expand Down
66 changes: 66 additions & 0 deletions libsol/string_utils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "include/sol/string_utils.h"

/**
* 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) {
if (!data) {
return false;
}
size_t i = 0;
while (i < length) {
if (data[i] < 0x80) {
/* 0xxxxxxx */
++i;
} else if ((data[i] & 0xe0) == 0xc0) {
/* 110XXXXx 10xxxxxx */
if (i + 1 >= length || (data[i + 1] & 0xc0) != 0x80 ||
(data[i] & 0xfe) == 0xc0) /* overlong? */ {
return false;
} else {
i += 2;
}
} else if ((data[i] & 0xf0) == 0xe0) {
/* 1110XXXX 10Xxxxxx 10xxxxxx */
if (i + 2 >= length || (data[i + 1] & 0xc0) != 0x80 || (data[i + 2] & 0xc0) != 0x80 ||
(data[i] == 0xe0 && (data[i + 1] & 0xe0) == 0x80) || /* overlong? */
(data[i] == 0xed && (data[i + 1] & 0xe0) == 0xa0) || /* surrogate? */
(data[i] == 0xef && data[i + 1] == 0xbf &&
(data[i + 2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ {
return false;
} else {
i += 3;
}
} else if ((data[i] & 0xf8) == 0xf0) {
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
if (i + 3 >= length || (data[i + 1] & 0xc0) != 0x80 || (data[i + 2] & 0xc0) != 0x80 ||
(data[i + 3] & 0xc0) != 0x80 ||
(data[i] == 0xf0 && (data[i + 1] & 0xf0) == 0x80) || /* overlong? */
(data[i] == 0xf4 && data[i + 1] > 0x8f) || data[i] > 0xf4) /* > U+10FFFF? */ {
return false;
} else {
i += 4;
}
} else {
return false;
}
}
return true;
}

/*
* Checks if data is in ASCII format
*/
bool is_data_ascii(const uint8_t *data, size_t length) {
if (!data) {
return false;
}
for (size_t i = 0; i < length; ++i) {
// Line feed char is accepted
if (((data[i] < 0x20 && data[i] != 0x0A) || data[i] > 0x7e)) {
return false;
}
}
return true;
}
97 changes: 97 additions & 0 deletions libsol/string_utils_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include "include/sol/string_utils.h"
#include <assert.h>
#include <stdio.h>
#include <stdbool.h>

void test_is_ascii() {
uint8_t message[] = "normal ascii text";
// don't count 0x00 byte at the end
assert(is_data_ascii(message, sizeof(message) - 1) == true);
}

void test_is_ascii_invalid_end_char() {
uint8_t message[] = "normal ascii text";

// Null terminated string should not be recognized as ascii
assert(is_data_ascii(message, sizeof(message)) == false);
}

void test_is_ascii_invalid_emoji() {
uint8_t message[] = "👍";

assert(is_data_ascii(message, sizeof(message)) == false);
}

void test_is_ascii_invalid_null() {
uint8_t *message = NULL;
assert(is_data_ascii(message, sizeof(message)) == false);
}

// test if emoji is going to be recognized as utf8 string
void test_is_utf8() {
uint8_t message[] = "👍";

assert(is_data_utf8(message, sizeof(message)) == true);
}

void test_is_utf8_2() {
uint8_t message[] = "żółć 안녕하세요 привет";

assert(is_data_ascii(message, sizeof(message) - 1) == false); // And we ignore null terminator
assert(is_data_utf8(message, sizeof(message)) == true);
}

void test_is_utf8_invalid_1() {
// Invalid Sequence Identifier
uint8_t message[] = {0xa0, 0xa1};

assert(is_data_utf8(message, sizeof(message)) == false);
}

void test_is_utf8_invalid_overlong() {
// Invalid UTF-8 (overlong)
uint8_t message[] = {0xC0, 0xAF};
uint8_t message2[] = {0xC0, 0x80};

assert(is_data_utf8(message, sizeof(message)) == false);
assert(is_data_utf8(message2, sizeof(message2)) == false);
}

void test_is_utf8_invalid_surrogate() {
// Invalid UTF-8 (surrogate)
uint8_t message[] = {0xED, 0xA0, 0x80};

assert(is_data_utf8(message, sizeof(message)) == false);
}

void test_is_utf8_invalid_3() {
uint8_t message1[] = {0x80}; // Invalid UTF-8 (starts with 10xxxxxx)
uint8_t message2[] = {0xF4, 0x90, 0x80, 0x80}; // Invalid UTF-8 (> U+10FFFF)

assert(is_data_utf8(message1, sizeof(message1)) == false);
assert(is_data_utf8(message2, sizeof(message2)) == false);
}

void test_is_utf8_invalid_null() {
uint8_t *message = NULL;

assert(is_data_utf8(message, sizeof(message)) == false);
}

int main() {
test_is_ascii();
test_is_ascii_invalid_end_char();
test_is_ascii_invalid_emoji();
test_is_ascii_invalid_null();

test_is_utf8();
test_is_utf8_2();
test_is_utf8_invalid_1();
test_is_utf8_invalid_overlong();
test_is_utf8_invalid_surrogate();
test_is_utf8_invalid_3();
test_is_utf8_invalid_null();

printf("passed\n");
return 0;
}
16 changes: 16 additions & 0 deletions libsol/transaction_summary.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct SummaryItem {
const char *string;
SizedString sized_string;
TokenAmount token_amount;
const OffchainMessageApplicationDomain *application_domain;
};
};

Expand Down Expand Up @@ -79,6 +80,15 @@ void summary_item_set_timestamp(SummaryItem *item, const char *title, int64_t va
item->i64 = value;
}

void summary_item_set_offchain_message_application_domain(
SummaryItem *item,
const char *title,
const OffchainMessageApplicationDomain *value) {
item->kind = SummaryItemOffchainMessageApplicationDomain;
item->title = title;
item->application_domain = value;
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New item required for showing app domain

typedef struct TransactionSummary {
SummaryItem primary;
SummaryItem fee_payer;
Expand Down Expand Up @@ -197,6 +207,12 @@ static int transaction_summary_update_display_for_item(const SummaryItem *item,
case SummaryItemTimestamp:
BAIL_IF(print_timestamp(item->i64, G_transaction_summary_text, TEXT_BUFFER_LENGTH));
break;
case SummaryItemOffchainMessageApplicationDomain:
BAIL_IF(encode_base58(item->application_domain,
OFFCHAIN_MESSAGE_APPLICATION_DOMAIN_LENGTH,
G_transaction_summary_text,
TEXT_BUFFER_LENGTH));
break;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handling new item type

}
print_string(item->title, G_transaction_summary_title, TITLE_SIZE);
return 0;
Expand Down
Loading
Loading