Skip to content

Commit

Permalink
Merge pull request #114 from LedgerHQ/develop
Browse files Browse the repository at this point in the history
Align master with P1
  • Loading branch information
fbeutin-ledger authored Feb 4, 2025
2 parents ff412d5 + 03c4a98 commit cf8b054
Show file tree
Hide file tree
Showing 280 changed files with 7,947 additions and 606 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1
with:
upload_app_binaries_artifact: compiled_app_binaries
flags: "TRUSTED_NAME_TEST_KEY=1"

ragger_tests:
name: Run ragger tests using the reusable workflow
Expand Down
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
skip: ./libsol/printer_test.c,./tests/Cargo.lock,./tools/apdu_generator/Cargo.lock
44 changes: 0 additions & 44 deletions .github/workflows/sonarcloud.yml

This file was deleted.

31 changes: 27 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ APPNAME = "Solana"

# Application version
APPVERSION_M = 1
APPVERSION_N = 5
APPVERSION_P = 4
APPVERSION_N = 7
APPVERSION_P = 0
APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)"

# Application source files
Expand All @@ -61,16 +61,20 @@ VARIANT_PARAM = COIN
VARIANT_VALUES = solana

# Enabling DEBUG flag will enable PRINTF and disable optimizations
#DEBUG = 1
# DEBUG = 1

########################################
# Application custom permissions #
########################################
HAVE_APPLICATION_FLAG_LIBRARY = 1
ifeq ($(TARGET_NAME),$(filter $(TARGET_NAME),TARGET_NANOX TARGET_STAX TARGET_FLEX))
HAVE_APPLICATION_FLAG_BOLOS_SETTINGS = 1
endif

########################################
# Swap features #
########################################
ENABLE_SWAP = 1

########################################
# Application communication interfaces #
########################################
Expand All @@ -90,6 +94,9 @@ DISABLE_STANDARD_APP_FILES = 1

# Allow usage of function from lib_standard_app/crypto_helpers.c
APP_SOURCE_FILES += ${BOLOS_SDK}/lib_standard_app/crypto_helpers.c
APP_SOURCE_FILES += ${BOLOS_SDK}/lib_standard_app/swap_utils.c
APP_SOURCE_FILES += ${BOLOS_SDK}/lib_standard_app/base58.c
CFLAGS += -I${BOLOS_SDK}/lib_standard_app/

WITH_U2F?=0
ifneq ($(WITH_U2F),0)
Expand All @@ -98,12 +105,28 @@ ifneq ($(WITH_U2F),0)
SDK_SOURCE_PATH += lib_u2f
endif

DEFINES += HAVE_SDK_TLV_PARSER

WITH_LIBSOL?=1
ifneq ($(WITH_LIBSOL),0)
SOURCE_FILES += $(filter-out %_test.c,$(wildcard libsol/*.c))
CFLAGS += -Ilibsol/include
CFLAGS += -Ilibsol
DEFINES += HAVE_SNPRINTF_FORMAT_U
DEFINES += NDEBUG
endif

#######################################
# Trusted Name Test Mode #
#######################################
TRUSTED_NAME_TEST_KEY ?= 0
ifneq ($(TRUSTED_NAME_TEST_KEY),0)
DEFINES += TRUSTED_NAME_TEST_KEY
endif

FIXED_TLV_CHALLENGE ?= 0
ifneq ($(FIXED_TLV_CHALLENGE),0)
DEFINES += FIXED_TLV_CHALLENGE
endif

include $(BOLOS_SDK)/Makefile.standard_app
49 changes: 49 additions & 0 deletions doc/api.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Solana application : Common Technical Specifications

## 1.4.0

- Add support of Trusted Name descriptor (checked by PKI certificate)

## 1.3.1

- Add support for versioned messages
Expand Down Expand Up @@ -134,6 +138,51 @@ _This command signs a Solana Off-Chain Message after having the user validate th
| ------------- | :------: |
| Signature | 64 |

### GET CHALLENGE

#### Description

_This command returns a 32-bit challenge generated by the app

##### Command

| _CLA_ | _INS_ | _P1_ | _P2_ | _Lc_ | _Le_ |
| ----- | :---: | ---: | ---- | :------: | -------: |
| E0 | 20 | 00 | 00 | 00 | N/A |

##### Input data

N/A

##### Output data

| _Description_ | _Length_ |
| ------------- | :------: |
| Challenge | 4 |

### PROVIDE TRUSTED NAME TLV DESCRIPTOR

#### Description

_This command provides a [Solana Trusted Name TLV descriptor](https://ledgerhq.atlassian.net/wiki/spaces/BE/pages/5123145859/Solana+token+account+ownership)

##### Command

| _CLA_ | _INS_ | _P1_ | _P2_ | _Lc_ | _Le_ |
| ----- | :---: | ---: | ---- | :------: | -------: |
| E0 | 21 | 00 | 00 | F7 (max) | variable |

##### Input data

| _Description_ | _Length_ |
| --------------------------------------------------- | :------: |
| Serialized signed TLV descriptor payload | variable |


##### Output data

N/A

## Transport protocol

### General transport description
Expand Down
6 changes: 5 additions & 1 deletion ledger_app.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
[app]
build_directory = "./"
sdk = "C"
devices = ["nanos", "nanox", "nanos+", "stax", "flex"]
devices = ["nanox", "nanos+", "stax", "flex"]

[use_cases]
trusted_name_test = "TRUSTED_NAME_TEST_KEY=1"
dbg_trusted_name_test = "DEBUG=1 TRUSTED_NAME_TEST_KEY=1"

[tests]
pytest_directory = "./tests/python"
155 changes: 155 additions & 0 deletions libsol/ed25519_helpers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#include <cx.h>

#include "common_byte_strings.h"

#include "ed25519_helpers.h"

static bool is_on_curve_internal(const uint8_t compressed_point[PUBKEY_LENGTH]) {
cx_ecpoint_t point;
cx_err_t result;
bool is_on_curve;
uint8_t compressed_point_tmp[PUBKEY_LENGTH];

memset(&point, 0, sizeof(point));
result = cx_ecpoint_alloc(&point, CX_CURVE_Ed25519);
if (result != CX_OK) {
PRINTF("cx_ecpoint_alloc failed %x\n", result);
return false;
}

// cx_decode_coord may hide the flag sign byte from the compressed point, we make a local copy
memcpy(compressed_point_tmp, compressed_point, PUBKEY_LENGTH);
int sign = cx_decode_coord(compressed_point_tmp, PUBKEY_LENGTH);
result = cx_ecpoint_decompress(&point, compressed_point_tmp, PUBKEY_LENGTH, sign);
if (result != CX_OK) {
PRINTF("cx_ecpoint_decompress failed %x\n", result);
return false;
}

result = cx_ecpoint_is_on_curve(&point, &is_on_curve);
if (result != CX_OK) {
PRINTF("cx_ecpoint_is_on_curve failed %x\n", result);
return false;
}
return is_on_curve;
}

static bool is_on_curve(const uint8_t compressed_point[PUBKEY_LENGTH]) {
CX_ASSERT(cx_bn_lock(PUBKEY_LENGTH, 0));
bool is_on_curve = is_on_curve_internal(compressed_point);
if (cx_bn_is_locked()) {
CX_ASSERT(cx_bn_unlock());
}

return is_on_curve;
}

static int derivate_ata_candidate(const uint8_t *owner_account,
const uint8_t *mint_account,
uint8_t nonce,
uint8_t (*derived_ata_candidate)[PUBKEY_LENGTH]) {
cx_sha256_t hash_ctx;
uint8_t program_id_spl_token[32] = {PROGRAM_ID_SPL_TOKEN};
uint8_t program_id_spl_associated_token_account[32] = {PROGRAM_ID_SPL_ASSOCIATED_TOKEN_ACCOUNT};
const char program_derived_address[] = "ProgramDerivedAddress";

cx_sha256_init(&hash_ctx);

if (cx_hash_no_throw((cx_hash_t *) &hash_ctx, 0, owner_account, PUBKEY_LENGTH, NULL, 0) !=
CX_OK) {
PRINTF("ERROR: Failed to hash owner account\n");
return -1;
}

if (cx_hash_no_throw((cx_hash_t *) &hash_ctx,
0,
program_id_spl_token,
PUBKEY_LENGTH,
NULL,
0) != CX_OK) {
PRINTF("ERROR: Failed to hash program ID\n");
return -1;
}

if (cx_hash_no_throw((cx_hash_t *) &hash_ctx, 0, mint_account, PUBKEY_LENGTH, NULL, 0) !=
CX_OK) {
PRINTF("ERROR: Failed to hash mint account\n");
return -1;
}

if (cx_hash_no_throw((cx_hash_t *) &hash_ctx, 0, &nonce, sizeof(nonce), NULL, 0) != CX_OK) {
PRINTF("ERROR: Failed to hash nonce\n");
return -1;
}

if (cx_hash_no_throw((cx_hash_t *) &hash_ctx,
0,
program_id_spl_associated_token_account,
PUBKEY_LENGTH,
NULL,
0) != CX_OK) {
PRINTF("ERROR: Failed to hash program ID\n");
return -1;
}

if (cx_hash_no_throw((cx_hash_t *) &hash_ctx,
0,
(const uint8_t *) program_derived_address,
strlen(program_derived_address),
NULL,
0) != CX_OK) {
PRINTF("ERROR: Failed to hash ProgramDerivedAddress string\n");
return -1;
}

if (cx_hash_no_throw((cx_hash_t *) &hash_ctx,
CX_LAST,
NULL,
0,
*derived_ata_candidate,
PUBKEY_LENGTH) != CX_OK) {
PRINTF("ERROR: Failed to finalize hash\n");
return -1;
}

return 0;
}

bool validate_associated_token_address(const uint8_t owner_account[PUBKEY_LENGTH],
const uint8_t mint_account[PUBKEY_LENGTH],
const uint8_t provided_ata[PUBKEY_LENGTH]) {
uint8_t derived_ata[PUBKEY_LENGTH];

// Start with the maximum nonce value
// As this is how official libraries do, we minimize the number of checks of valid case
uint8_t nonce = 255;

PRINTF("Trying to validate provided_ata %.*H\n", PUBKEY_LENGTH, provided_ata);
while (nonce > 0) {
// Worst case scenario is 255 hash + 255 memcmp. The performance hit is not noticeable.
if (derivate_ata_candidate(owner_account, mint_account, nonce, &derived_ata) != 0) {
PRINTF("Error derivate_ata_candidate for nonce %d\n", nonce);
return false;
}
// Compare the derived ATA with the provided ATA
PRINTF("derived_ata %.*H with nonce%d\n", PUBKEY_LENGTH, derived_ata, nonce);
nonce--;

if (memcmp(derived_ata, provided_ata, PUBKEY_LENGTH) == 0) {
PRINTF("Successful ATA match\n");
// A valid ATA cannot be on the curve, check that the one we received is valid
// Official online libraries do it before the memcmp
// we do it after to do it at most once
if (is_on_curve(derived_ata)) {
PRINTF("Error, derived ATA is on the curve\n");
return false;
} else {
return true;
}
}
}

// We exhausted all nonces without matching the provided ATA
PRINTF("ERROR: Unable to find a valid nonce for ATA derivation\n");
return false;
}
7 changes: 7 additions & 0 deletions libsol/ed25519_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include "globals.h"

bool validate_associated_token_address(const uint8_t owner_account[PUBKEY_LENGTH],
const uint8_t mint_account[PUBKEY_LENGTH],
const uint8_t provided_ata[PUBKEY_LENGTH]);
18 changes: 18 additions & 0 deletions libsol/include/sol/trusted_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include "sol/printer.h"

// libsol cannot include src.
// The libsol / src split should be reevaluated, in the meantime this lives here

typedef struct trusted_info_s {
bool received;
char encoded_owner_address[BASE58_PUBKEY_LENGTH];
uint8_t owner_address[PUBKEY_LENGTH];
char encoded_token_address[BASE58_PUBKEY_LENGTH];
uint8_t token_address[PUBKEY_LENGTH];
char encoded_mint_address[BASE58_PUBKEY_LENGTH];
uint8_t mint_address[PUBKEY_LENGTH];
} trusted_info_t;

extern trusted_info_t g_trusted_info;
1 change: 0 additions & 1 deletion libsol/printer.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ int print_token_amount(uint64_t amount,
return 0;
}

#define SOL_DECIMALS 9
int print_amount(uint64_t amount, char *out, size_t out_length) {
return print_token_amount(amount, "SOL", SOL_DECIMALS, out, out_length);
}
Expand Down
Loading

0 comments on commit cf8b054

Please sign in to comment.