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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/swap-ci-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
name: Swap functional tests

on:
workflow_dispatch:
push:
branches:
- master
- develop
pull_request:

jobs:
job_functional_tests:
uses: LedgerHQ/app-exchange/.github/workflows/reusable_swap_functional_tests.yml@develop
with:
repo_for_ton: ${{ github.repository }}
branch_for_ton: ${{ github.ref }}
test_filter: '"Ton or ton"'
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ endif

APPNAME = "TON"
APPVERSION_M = 2
APPVERSION_N = 3
APPVERSION_P = 0
APPVERSION_N = 4
APPVERSION_P = 1
APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)"

APP_SOURCE_PATH += src
Expand All @@ -53,14 +53,15 @@ VARIANT_VALUES = TON
# HAVE_APPLICATION_FLAG_BOLOS_SETTINGS = 1
# HAVE_APPLICATION_FLAG_LIBRARY = 1

ENABLE_SWAP = 1
ENABLE_BLUETOOTH = 1
# ENABLE_NFC = 1

ENABLE_NBGL_QRCODE = 1
# ENABLE_NBGL_KEYBOARD = 1
# ENABLE_NBGL_KEYPAD = 1

# DISABLE_STANDARD_APP_FILES = 1
# DISABLE_STANDARD_APP_FILES = 1
# DISABLE_DEFAULT_IO_SEPROXY_BUFFER_SIZE = 1 # To allow custom size declaration
# DISABLE_STANDARD_APP_DEFINES = 1 # Will set all the following disablers
# DISABLE_STANDARD_SNPRINTF = 1
Expand All @@ -71,3 +72,5 @@ ENABLE_NBGL_QRCODE = 1
# DISABLE_DEBUG_THROW = 1

include $(BOLOS_SDK)/Makefile.standard_app

DEFINES += HAVE_BOLOS_APP_STACK_CANARY
98 changes: 98 additions & 0 deletions src/common/base64.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <stddef.h> // size_t
#include <stdint.h> // uint*_t

#include <string.h>
#include "base64.h"

static const char base64_alphabet[] =
Expand Down Expand Up @@ -31,3 +32,100 @@ int base64_encode(const uint8_t *data, size_t data_length, char *out, size_t out
out[out_length] = '\0';
return out_length;
}

const uint8_t pr2six[256] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 63, //
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, //
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, //
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63, //
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, //
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, //
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 //
};

// Taken from Exchange application implementation
int base64url_decode(const char *src, size_t src_len, uint8_t *dest, size_t dest_len) {
const uint8_t *original_dest;

original_dest = dest;

if (src_len % 4 == 1) {
return -1;
} else if (src_len == 0) {
return 0;
}

while (src_len > 4) {
if (dest_len < 3) {
return -1;
}
*(dest++) = (pr2six[(uint8_t) src[0]] << 2 | pr2six[(uint8_t) src[1]] >> 4);
*(dest++) = (pr2six[(uint8_t) src[1]] << 4 | pr2six[(uint8_t) src[2]] >> 2);
*(dest++) = (pr2six[(uint8_t) src[2]] << 6 | pr2six[(uint8_t) src[3]] >> 0);
src += 4;
src_len -= 4;
dest_len -= 3;
}

if (src_len > 1) {
if (dest_len < 1) {
return -1;
}
*(dest++) = (pr2six[(uint8_t) src[0]] << 2 | pr2six[(uint8_t) src[1]] >> 4);
dest_len--;
}

if (src_len > 2) {
if (dest_len < 1) {
return -1;
}
*(dest++) = (pr2six[(uint8_t) src[1]] << 4 | pr2six[(uint8_t) src[2]] >> 2);
dest_len--;
}

if (src_len > 3) {
if (dest_len < 1) {
return -1;
}
*(dest++) = (pr2six[(uint8_t) src[2]] << 6 | pr2six[(uint8_t) src[3]] >> 0);
}

return dest - original_dest;
}

int base64_to_base64url(const char *src, size_t src_len, char *dest, size_t dest_len) {
if (dest_len < src_len) {
return -1;
}
explicit_bzero(dest, dest_len);

size_t i;
for (i = 0; i < src_len; i++) {
switch (src[i]) {
case '+':
dest[i] = '-';
break;
case '/':
dest[i] = '_';
break;
case '=':
// Padding character only found at the end, we simply remove it
dest[i] = '\0';
return i;
default:
dest[i] = src[i]; // Copy other characters directly
break;
}
}

return src_len;
}
54 changes: 54 additions & 0 deletions src/common/base64.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,57 @@
*
*/
int base64_encode(const uint8_t *data, size_t data_length, char *out, size_t out_len);

/**
* Decode a Base64URL encoded string.
*
* This function decodes a Base64URL encoded string into its original binary form.
* The Base64URL encoding is similar to standard Base64, but uses different characters
* for URL-safe encoding, as defined in RFC 4648.
*
* @see https://datatracker.ietf.org/doc/html/rfc4648
*
* @param[in] src
* Pointer to the input Base64URL encoded string.
* @param[in] src_len
* The length of the Base64URL encoded string.
* @param[out] dest
* Pointer to the output buffer where the decoded bytes will be stored.
* @param[in] dest_len
* The maximum size of the output buffer.
*
* @return The number of bytes successfully decoded and stored in the output buffer,
* or -1 if an error occurs
*
*/
int base64url_decode(const char *src, size_t src_len, uint8_t *dest, size_t dest_len);

/**
* Convert a Base64 encoded string to a Base64URL encoded string.
*
* This function converts a given Base64 encoded string to a Base64URL encoded
* string by replacing certain characters and removing any padding characters ('=')
* at the end of the string. The result is stored in the provided destination buffer.
*
* Base64URL encoding is defined in RFC 4648 and is a URL-safe variant of Base64 encoding,
* where '+' is replaced with '-' and '/' is replaced with '_'.
*
* @see https://datatracker.ietf.org/doc/html/rfc4648
*
* @param[in] src
* Pointer to the buffer containing the Base64 encoded string.
* @param[in] src_len
* Length of the Base64 encoded string in the source buffer.
* @param[out] dest
* Pointer to the buffer where the Base64URL encoded string will be stored.
* The buffer should be large enough to hold the result, which is typically
* the same size as the source buffer.
*
* @param[in] dest_len
* Maximum length of the destination buffer.
*
* @return Number of characters written to the destination buffer, excluding the null terminator,
* or -1 if the destination buffer is too small.
*
*/
int base64_to_base64url(const char *src, size_t src_len, char *dest, size_t dest_len);
10 changes: 7 additions & 3 deletions src/common/bip32_check.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@

#include "bip32_check.h"

bool check_global_bip32_path() {
if (G_context.bip32_path_len <= 2) return false;
bool check_bip32_path(uint8_t bip32_path_len, const uint32_t bip32_path[MAX_BIP32_PATH]) {
if (bip32_path_len <= 2) return false;

return bip32_path[0] == 0x8000002c && bip32_path[1] == 0x8000025f;
}

return G_context.bip32_path[0] == 0x8000002c && G_context.bip32_path[1] == 0x8000025f;
bool check_global_bip32_path() {
return check_bip32_path(G_context.bip32_path_len, G_context.bip32_path);
}
15 changes: 14 additions & 1 deletion src/common/bip32_check.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@

#include <stdbool.h> // bool

#include "../types.h" // MAX_BIP32_PATH

/**
* Check if the given BIP32 path length and path array are valid.
*
* @param bip32_path_len The length of the BIP32 path.
* @param bip32_path The array containing the BIP32 path.
*
* @return true if the BIP32 path is valid, false otherwise.
*
*/
bool check_bip32_path(uint8_t bip32_path_len, const uint32_t bip32_path[MAX_BIP32_PATH]);

/**
* Check the bip32 path stored in G_context.
*
* @return true if bip32 path is valid, false otherwise
* @return true if the BIP32 path is valid, false otherwise.
*
*/
bool check_global_bip32_path();
6 changes: 3 additions & 3 deletions src/common/format_address.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ bool address_to_friendly(const uint8_t chain,

// Address Tag
if (bounceable) {
out[0] = 0x11; // Bounceable
out[0] = BOUNCEABLE;
} else {
out[0] = 0x51; // Non-Bounceable
out[0] = NON_BOUNCEABLE;
}
if (testOnly) {
out[0] = out[0] | 0x80;
out[0] = out[0] | TESTNET_ONLY;
}

// Workchain
Expand Down
4 changes: 4 additions & 0 deletions src/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,7 @@
* Max length for cell_inline types
*/
#define MAX_CELL_INLINE_LEN 32

#define BOUNCEABLE 0x11
#define NON_BOUNCEABLE 0x51
#define TESTNET_ONLY 0x80
57 changes: 38 additions & 19 deletions src/handler/get_public_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,34 +34,53 @@
#include "../helper/send_response.h"
#include "../apdu/params.h"

int handler_get_public_key(uint8_t flags, buffer_t *cdata, bool display) {
explicit_bzero(&G_context, sizeof(G_context));
G_context.req_type = CONFIRM_ADDRESS;
G_context.state = STATE_NONE;

if (!buffer_read_u8(cdata, &G_context.bip32_path_len) ||
!buffer_read_bip32_path(cdata, G_context.bip32_path, (size_t) G_context.bip32_path_len)) {
return io_send_sw(SW_WRONG_DATA_LENGTH);
int get_public_key_helper(uint8_t flags,
buffer_t *cdata,
uint8_t *bip32_path_len,
uint32_t bip32_path[MAX_BIP32_PATH],
pubkey_ctx_t *pk_info) {
if (!buffer_read_u8(cdata, bip32_path_len) ||
!buffer_read_bip32_path(cdata, bip32_path, (size_t) *bip32_path_len)) {
PRINTF("Error SW_WRONG_DATA_LENGTH\n");
return SW_WRONG_DATA_LENGTH;
}

if (!check_global_bip32_path()) {
return io_send_sw(SW_BAD_BIP32_PATH);
if (!check_bip32_path(*bip32_path_len, bip32_path)) {
PRINTF("Error SW_BAD_BIP32_PATH\n");
return SW_BAD_BIP32_PATH;
}

if (crypto_derive_public_key(G_context.bip32_path,
G_context.bip32_path_len,
G_context.pk_info.raw_public_key) < 0) {
return io_send_sw(SW_BAD_STATE);
if (crypto_derive_public_key(bip32_path, *bip32_path_len, pk_info->raw_public_key) < 0) {
PRINTF("Error SW_BAD_STATE\n");
return SW_BAD_STATE;
}

if (flags & P2_ADDR_FLAG_WALLET_SPECIFIERS) {
if (!buffer_read_bool(cdata, &G_context.pk_info.is_v3r2) ||
!buffer_read_u32(cdata, &G_context.pk_info.subwallet_id, BE)) {
return io_send_sw(SW_WRONG_DATA_LENGTH);
if (!buffer_read_bool(cdata, &pk_info->is_v3r2) ||
!buffer_read_u32(cdata, &pk_info->subwallet_id, BE)) {
PRINTF("Error SW_WRONG_DATA_LENGTH\n");
return SW_WRONG_DATA_LENGTH;
}
} else {
G_context.pk_info.subwallet_id = 698983191;
G_context.pk_info.is_v3r2 = false;
pk_info->subwallet_id = 698983191;
pk_info->is_v3r2 = false;
}

return 0;
}

int handler_get_public_key(uint8_t flags, buffer_t *cdata, bool display) {
explicit_bzero(&G_context, sizeof(G_context));
G_context.req_type = CONFIRM_ADDRESS;
G_context.state = STATE_NONE;

int ret = get_public_key_helper(flags,
cdata,
&G_context.bip32_path_len,
G_context.bip32_path,
&G_context.pk_info);
if (ret != 0) {
return io_send_sw(ret);
}

if (display) {
Expand Down
24 changes: 24 additions & 0 deletions src/handler/get_public_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,32 @@
#include <stdbool.h> // bool
#include <stdint.h> // uint*_t

#include "../types.h"
#include "common/mybuffer.h"

/**
* Helper for GET_PUBLIC_KEY handler. If successfully parse BIP32 path and derive the public key.
*
* @param[in] flags
* Address display flags
* @param[in,out] cdata
* Command data with BIP32 path.
* @param[out] bip32_path_len
* Requested bip32_path to derive the public key on length
* @param[out] bip32_path
* Requested bip32_path to derive the public key on
* @param[out] pk_info
* Additional info to derive the public key with
*
* @return zero if success, error code otherwise.
*
*/
int get_public_key_helper(uint8_t flags,
buffer_t *cdata,
uint8_t *bip32_path_len,
uint32_t bip32_path[MAX_BIP32_PATH],
pubkey_ctx_t *pk_info);

/**
* Handler for GET_PUBLIC_KEY command. If successfully parse BIP32 path,
* derive public key and send APDU response.
Expand Down
Loading