Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding the Swap feature #6

Merged
merged 10 commits into from
Sep 27, 2023
18 changes: 18 additions & 0 deletions .github/workflows/swap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Swap feature tests

on:
workflow_dispatch:
push:
branches:
- main
pull_request:

jobs:
job_functional_tests:
uses: spalmer25/app-exchange/.github/workflows/reusable_swap_functional_tests.yml@palmer/functori/add-new-tezos-app-swap-tests
with:
repo_for_exchange: 'spalmer25/app-exchange'
branch_for_exchange: 'palmer/functori/add-new-tezos-app-swap-tests'
branch_for_tezos: ${{ github.ref }}
run_for_devices: '["nanos", "nanosp"]'
test_filter: '"tezos_new"'
1 change: 1 addition & 0 deletions app/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ CFLAGS += -Wwrite-strings
########################################
ENABLE_BLUETOOTH = 1
#ENABLE_NFC = 1
ENABLE_SWAP = 1

########################################
# NBGL custom features #
Expand Down
25 changes: 2 additions & 23 deletions app/src/apdu_pubkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,29 +66,8 @@ provide_pubkey(void)
static void
format_pkh(char *buffer, size_t len)
{
cx_ecfp_public_key_t pubkey = {0};
uint8_t hash[21];
TZ_PREAMBLE(("buffer=%p, len=%u", buffer, len));

TZ_CHECK(generate_public_key(&pubkey,
global.path_with_curve.derivation_type,
&global.path_with_curve.bip32_path));
// clang-format off
TZ_CHECK(public_key_hash(hash+1, 20, NULL,
global.path_with_curve.derivation_type, &pubkey));
switch (global.path_with_curve.derivation_type) {
case DERIVATION_TYPE_SECP256K1: hash[0] = 1; break;
case DERIVATION_TYPE_SECP256R1: hash[0] = 2; break;
case DERIVATION_TYPE_ED25519:
case DERIVATION_TYPE_BIP32_ED25519: hash[0] = 0; break;
default: CX_CHECK(EXC_WRONG_PARAM); break;
}
// clang-format on

if (tz_format_pkh(hash, 21, buffer, len))
TZ_FAIL(EXC_UNKNOWN);

TZ_POSTAMBLE;
derive_pkh(global.path_with_curve.derivation_type,
&global.path_with_curve.bip32_path, buffer, len);
}

static void
Expand Down
21 changes: 20 additions & 1 deletion app/src/apdu_sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
#include "parser/parser_state.h"
#include "parser/operation_parser.h"

#ifdef HAVE_SWAP
#include <swap.h>

#include "handle_swap.h"
#endif // HAVE_SWAP

#ifdef HAVE_NBGL
#include "nbgl_use_case.h"
#endif
Expand Down Expand Up @@ -193,6 +199,7 @@ refill()
break;
case TZ_ERR_INVALID_TAG:
case TZ_ERR_INVALID_OP:
case TZ_ERR_INVALID_DATA:
case TZ_ERR_UNSUPPORTED:
case TZ_ERR_TOO_LARGE:
case TZ_ERR_TOO_DEEP:
Expand Down Expand Up @@ -222,6 +229,7 @@ send_cancel(void)
break;
case TZ_ERR_INVALID_TAG:
case TZ_ERR_INVALID_OP:
case TZ_ERR_INVALID_DATA:
case TZ_ERR_UNSUPPORTED:
case TZ_ERR_TOO_LARGE:
case TZ_ERR_TOO_DEEP:
Expand All @@ -241,7 +249,18 @@ stream_cb(tz_ui_cb_type_t type)

// clang-format off
switch (type) {
case TZ_UI_STREAM_CB_ACCEPT: return sign_packet();
case TZ_UI_STREAM_CB_ACCEPT:
#ifdef HAVE_SWAP
if (G_called_from_swap) {
if (G_swap_response_ready)
os_sched_exit(-1);
else
G_swap_response_ready = true;

TZ_CHECK(swap_check_validity());
}
#endif // HAVE_SWAP
return sign_packet();
case TZ_UI_STREAM_CB_REFILL: return refill();
case TZ_UI_STREAM_CB_REJECT: return send_reject();
case TZ_UI_STREAM_CB_CANCEL: return send_cancel();
Expand Down
247 changes: 247 additions & 0 deletions app/src/handle_swap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/* Tezos Ledger application - Swap requirement

Copyright 2023 Nomadic Labs <[email protected]>
Copyright 2023 TriliTech <[email protected]>
Copyright 2023 Functori <[email protected]>

With code excerpts from:
- Legacy Tezos app, Copyright 2019 Obsidian Systems
- Ledger Blue sample apps, Copyright 2016 Ledger

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#ifdef HAVE_SWAP

#include <format.h>
#include <swap.h>

#include "compat.h"
#include "handle_swap.h"
#include "keys.h"
#include "utils.h"

#include "parser/num_parser.h"

// based on app-exchange
#define TICKER_MAX_SIZE 9
#define ADDRESS_MAX_SIZE 63

/* Check check_address_parameters_t.address_to_check against specified
* parameters.
*
* Must set params.result to 0 on error, 1 otherwise */
void
swap_handle_check_address(check_address_parameters_t *params)
{
FUNC_ENTER(("params=%p", params));

if (params->address_to_check == NULL) {
PRINTF("[ERROR] Address to check is null\n");
goto error;
}

if (params->address_parameters_length == 0
|| params->address_parameters == NULL) {
PRINTF("[ERROR] Address parameters is null\n");
goto error;
}

if (params->extra_id_to_check == NULL) {
PRINTF("[ERROR] Extra id is null\n");
goto error;
}

if (params->extra_id_to_check[0] != '\0') {
PRINTF("[ERROR] Extra id is not empty: %s\n",
params->extra_id_to_check);
goto error;
}

char address[TZ_CAPTURE_BUFFER_SIZE] = {0};

// Always tz1
derivation_type_t derivation_type = DERIVATION_TYPE_ED25519;
bip32_path_t bip32_path;

TZ_CHECK(read_bip32_path(&bip32_path, params->address_parameters,
params->address_parameters_length));

TZ_CHECK(
derive_pkh(derivation_type, &bip32_path, address, sizeof(address)));

if (strcmp(params->address_to_check, address) != 0) {
PRINTF("[ERROR] Check address fail: %s != %s\n",
params->address_to_check, address);
goto error;
}

params->result = 1;
FUNC_LEAVE();
return;
error:
bail:
params->result = 0;
FUNC_LEAVE();
}

/* Format printable amount including the ticker from specified parameters.
*
* Must set empty printable_amount on error, printable amount otherwise */
void
swap_handle_get_printable_amount(get_printable_amount_parameters_t *params)
{
FUNC_ENTER(("params=%p", params));

uint64_t amount;
uint8_t decimals;
char ticker[TICKER_MAX_SIZE];

if (!swap_parse_config(params->coin_configuration,
params->coin_configuration_length, ticker,
sizeof(ticker), &decimals)) {
PRINTF("[ERROR] Fail to parse config\n");
goto error;
}

if (!swap_str_to_u64(params->amount, params->amount_length, &amount)) {
PRINTF("[ERROR] Fail to parse amount\n");
goto error;
}

if (!format_fpu64_trimmed(params->printable_amount,
sizeof(params->printable_amount), amount,
decimals)) {
PRINTF("[ERROR] Fail to print amount\n");
goto error;
}

strlcat(params->printable_amount, " ", sizeof(params->printable_amount));
strlcat(params->printable_amount, ticker,
sizeof(params->printable_amount));

FUNC_LEAVE();
return;

error:
memset(params->printable_amount, '\0', sizeof(params->printable_amount));
FUNC_LEAVE();
}

typedef struct {
uint64_t amount;
uint64_t fee;
char destination_address[ADDRESS_MAX_SIZE];
} swap_transaction_parameters_t;

static swap_transaction_parameters_t G_swap_params;

static uint8_t *G_swap_transaction_result;

/* Backup up transaction parameters and wipe BSS to avoid collusion with
* app-exchange BSS data.
*
* return false on error, true otherwise */
bool
swap_copy_transaction_parameters(create_transaction_parameters_t *params)
{
FUNC_ENTER(("params=%p", params));

swap_transaction_parameters_t params_copy;
memset(&params_copy, 0, sizeof(params_copy));

if (!swap_str_to_u64(params->amount, params->amount_length,
&params_copy.amount)) {
PRINTF("[ERROR] Fail to parse amount\n");
goto error;
}

if (!swap_str_to_u64(params->fee_amount, params->fee_amount_length,
&params_copy.fee)) {
PRINTF("[ERROR] Fail to parse fee\n");
goto error;
}

if (params->destination_address == NULL) {
PRINTF("[ERROR] Destination address is null\n");
goto error;
}

strlcpy(params_copy.destination_address, params->destination_address,
sizeof(params_copy.destination_address));
if (params_copy
.destination_address[sizeof(params_copy.destination_address) - 1]
!= '\0') {
PRINTF("[ERROR] Fail to copy destination address\n");
goto error;
}

if (params->destination_address_extra_id == NULL) {
PRINTF("[ERROR] Destination address extra id is null\n");
goto error;
}

if (params->destination_address_extra_id[0] != '\0') {
PRINTF("[ERROR] Destination address extra id is not empty: %s\n",
params->destination_address_extra_id);
goto error;
}

os_explicit_zero_BSS_segment();

G_swap_transaction_result = &params->result;

memcpy(&G_swap_params, &params_copy, sizeof(params_copy));

FUNC_LEAVE();
return true;

error:
FUNC_LEAVE();
return false;
}

void
swap_check_validity(void)
{
tz_operation_state *op = &global.apdu.sign.u.clear.parser_state.operation;
char dstaddr[ADDRESS_MAX_SIZE];
TZ_PREAMBLE((""));

PRINTF("[DEBUG] batch_index = %u, tag=%d\n", op->batch_index,
op->last_tag);
TZ_ASSERT(EXC_REJECT, op->batch_index == 1);
TZ_ASSERT(EXC_REJECT, op->last_tag == TZ_OPERATION_TAG_TRANSACTION);
TZ_ASSERT(EXC_REJECT, op->last_amount == G_swap_params.amount);
TZ_ASSERT(EXC_REJECT, op->last_fee == G_swap_params.fee);

tz_format_address(op->destination, 22, dstaddr, sizeof(dstaddr));

PRINTF("[DEBUG] dstaddr=\"%s\"\n", dstaddr);
PRINTF("[DEBUG] G...dstaddr=\"%s\"\n", G_swap_params.destination_address);
TZ_ASSERT(EXC_REJECT,
!strcmp(dstaddr, G_swap_params.destination_address));

TZ_POSTAMBLE;
}

/* Set create_transaction.result and call os_lib_end().
*
* Doesn't return */
void __attribute__((noreturn))
swap_finalize_exchange_sign_transaction(bool is_success)
{
*G_swap_transaction_result = is_success;
os_lib_end();
}

#endif // HAVE_SWAP
25 changes: 25 additions & 0 deletions app/src/handle_swap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Tezos Ledger application - Swap requirement

Copyright 2023 Nomadic Labs <[email protected]>
Copyright 2023 TriliTech <[email protected]>
Copyright 2023 Functori <[email protected]>

With code excerpts from:
- Legacy Tezos app, Copyright 2019 Obsidian Systems
- Ledger Blue sample apps, Copyright 2016 Ledger

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#ifdef HAVE_SWAP
void swap_check_validity(void);
#endif // HAVE_SWAP
Loading