Skip to content
Open
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
  •  
  •  
  •  
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ APPNAME = "Solana"
# Application version
APPVERSION_M = 1
APPVERSION_N = 7
APPVERSION_P = 1
APPVERSION_P = 2
APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)"

# Application source files
Expand Down
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;
8 changes: 8 additions & 0 deletions libsol/include/sol/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,17 @@ typedef struct MessageHeader {
size_t instructions_length;
} MessageHeader;

#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);
7 changes: 7 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 All @@ -68,6 +70,7 @@ SummaryItem *transaction_summary_fee_payer_item();
SummaryItem *transaction_summary_nonce_account_item();
SummaryItem *transaction_summary_nonce_authority_item();
SummaryItem *transaction_summary_general_item();
SummaryItem *transaction_summary_primary_or_general_item();

int transaction_summary_set_fee_payer_pubkey(const Pubkey *pubkey);

Expand All @@ -85,3 +88,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) {
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;
}
Loading
Loading