Skip to content

Commit

Permalink
support swap functions
Browse files Browse the repository at this point in the history
  • Loading branch information
chcmedeiros committed Dec 20, 2024
1 parent b301d7e commit 7039892
Show file tree
Hide file tree
Showing 9 changed files with 448 additions and 7 deletions.
4 changes: 2 additions & 2 deletions app/Makefile.version
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This is the `transaction_version` field of `Runtime`
APPVERSION_M=2
# This is the `spec_version` field of `Runtime`
APPVERSION_N=35
APPVERSION_N=36
# This is the patch version of this release
APPVERSION_P=27
APPVERSION_P=0
43 changes: 38 additions & 5 deletions app/src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ address_encoding_e encoding = BECH32_COSMOS;

#include "cx.h"

static zxerr_t crypto_extractUncompressedPublicKey(uint8_t *pubKey, uint16_t pubKeyLen) {
static zxerr_t crypto_extractUncompressedPublicKey(uint8_t *pubKey, uint16_t pubKeyLen, uint32_t *hdPath_to_use, uint16_t hdPath_to_use_len) {
if (pubKey == NULL || pubKeyLen < PK_LEN_SECP256K1_UNCOMPRESSED) {
return zxerr_invalid_crypto_settings;
}
Expand All @@ -46,8 +46,8 @@ static zxerr_t crypto_extractUncompressedPublicKey(uint8_t *pubKey, uint16_t pub
// Generate keys
CATCH_CXERROR(os_derive_bip32_with_seed_no_throw(HDW_NORMAL,
CX_CURVE_256K1,
hdPath,
HDPATH_LEN_DEFAULT,
hdPath_to_use,
hdPath_to_use_len,
privateKeyData,
NULL,
NULL,
Expand Down Expand Up @@ -155,14 +155,14 @@ zxerr_t crypto_sign(uint8_t *output,
return error;
}

zxerr_t crypto_fillAddress(uint8_t *buffer, uint16_t buffer_len, uint16_t *addrResponseLen) {
zxerr_t crypto_fillAddress_helper(uint8_t *buffer, uint16_t buffer_len, uint16_t *addrResponseLen, uint32_t *hdPath_to_use, uint16_t hdPath_to_use_len) {
if (buffer_len < PK_LEN_SECP256K1 + 50) {
return zxerr_buffer_too_small;
}

// extract pubkey
uint8_t uncompressedPubkey [PK_LEN_SECP256K1_UNCOMPRESSED] = {0};
CHECK_ZXERR(crypto_extractUncompressedPublicKey(uncompressedPubkey, sizeof(uncompressedPubkey)));
CHECK_ZXERR(crypto_extractUncompressedPublicKey(uncompressedPubkey, sizeof(uncompressedPubkey), hdPath_to_use, hdPath_to_use_len));
CHECK_ZXERR(compressPubkey(uncompressedPubkey, sizeof(uncompressedPubkey), buffer, buffer_len));
char *addr = (char *) (buffer + PK_LEN_SECP256K1);

Expand Down Expand Up @@ -193,3 +193,36 @@ zxerr_t crypto_fillAddress(uint8_t *buffer, uint16_t buffer_len, uint16_t *addrR

return zxerr_ok;
}

// fill a crypto address using the global hdpath
zxerr_t crypto_fillAddress(uint8_t *buffer, uint16_t buffer_len, uint16_t *addrResponseLen){
return crypto_fillAddress_helper(buffer, buffer_len, addrResponseLen, hdPath, HDPATH_LEN_DEFAULT);
}

// Fill address using a hd path coming from check_address_parameters_t
zxerr_t crypto_swap_fillAddress(uint32_t *hdPath_swap,
uint8_t hdPathLen_swap,
char *buffer,
uint16_t bufferLen,
uint16_t *addrResponseLen) {
if (bufferLen < 50) {
return zxerr_buffer_too_small;
}

// extract pubkey
uint8_t uncompressedPubkey [PK_LEN_SECP256K1_UNCOMPRESSED] = {0};
uint8_t compressedPubkey [PK_LEN_SECP256K1] = {0};
CHECK_ZXERR(crypto_extractUncompressedPublicKey(uncompressedPubkey, sizeof(uncompressedPubkey), hdPath_swap, hdPathLen_swap));
CHECK_ZXERR(compressPubkey(uncompressedPubkey, sizeof(uncompressedPubkey), compressedPubkey, sizeof(compressedPubkey)));

uint8_t hashed1_pk[CX_SHA256_SIZE] = {0};
cx_hash_sha256(compressedPubkey, PK_LEN_SECP256K1, hashed1_pk, CX_SHA256_SIZE);

uint8_t hashed2_pk[CX_RIPEMD160_SIZE] = {0};
CHECK_CX_OK(cx_ripemd160_hash(hashed1_pk, CX_SHA256_SIZE, hashed2_pk));
// support only cosmos for now we might need to send the hrp as an address parameter
CHECK_ZXERR(bech32EncodeFromBytes(buffer, bufferLen, "cosmos", hashed2_pk, CX_RIPEMD160_SIZE, 1, BECH32_ENCODING_BECH32));

*addrResponseLen = strnlen(buffer, bufferLen);
return zxerr_ok;
}
5 changes: 5 additions & 0 deletions app/src/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ zxerr_t crypto_fillAddress(uint8_t *buffer, uint16_t bufferLen, uint16_t *addrRe

zxerr_t crypto_sign(uint8_t *signature, uint16_t signatureMaxlen, uint16_t *signatureLen);

zxerr_t crypto_swap_fillAddress(uint32_t *hdPath_swap,
uint8_t hdPathLen_swap,
char *buffer,
uint16_t bufferLen,
uint16_t *addrResponseLen);
#ifdef __cplusplus
}
#endif
63 changes: 63 additions & 0 deletions app/src/swap/handle_check_address.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*******************************************************************************
* (c) 2016 Ledger
* (c) 2018 - 2023 Zondax AG
*
* 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.
********************************************************************************/
#include "crypto.h"
#include "lib_standard_app/swap_lib_calls.h"
#include "lib_standard_app/bip32.h"
#include "swap.h"
#include "zxformat.h"
#include "swap_utils.h"


void handle_check_address(check_address_parameters_t *params) {
if (params == NULL || params->address_to_check == 0) {
return;
}

// Reset result
params->result = 0;

// Address parameters have the following structure
// 00 byte |path length (1 byte) | bip32 path (4 * pathLength bytes)
// Get the path
uint32_t bip32_path[HDPATH_LEN_DEFAULT] = {0};
uint8_t bip32_path_length = params->address_parameters[1];

if (bip32_path_length != HDPATH_LEN_DEFAULT) {
return;
}

for (uint32_t i = 0; i < HDPATH_LEN_DEFAULT; i++) {
readU32BE(params->address_parameters + 2 + (i * 4), &bip32_path[i]);
}

char address_computed[100] = {0};
uint16_t reply_len = 0;
zxerr_t err = crypto_swap_fillAddress(bip32_path,
bip32_path_length, address_computed, sizeof(address_computed),
&reply_len);
if (err != zxerr_ok) {
MEMZERO(address_computed, sizeof(address_computed));
return;
}

// Exchange guarantees that the input string is '\0' terminated
uint8_t address_to_check_len = strlen(params->address_to_check);

if (reply_len == address_to_check_len && memcmp(address_computed, params->address_to_check, reply_len) == 0) {
params->result = 1;
}
}
46 changes: 46 additions & 0 deletions app/src/swap/handle_get_printable_amount.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*******************************************************************************
* (c) 2016 Ledger
* (c) 2018 - 2023 Zondax AG
*
* 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.
********************************************************************************/
#include "bignum.h"
#include "crypto.h"
#include "lib_standard_app/swap_lib_calls.h"
#include "swap.h"
#include "swap_utils.h"
#include "zxformat.h"

void handle_get_printable_amount(get_printable_amount_parameters_t *params) {
if (params == NULL) {
return;
}

uint8_t amount[COIN_AMOUNT_MAXSIZE] = {0};
MEMZERO(amount, sizeof(amount));
MEMZERO(params->printable_amount, sizeof(params->printable_amount));

if (params->amount_length > COIN_AMOUNT_MAXSIZE) {
return;
}

memcpy(amount + COIN_AMOUNT_MAXSIZE - params->amount_length, params->amount, params->amount_length);

char tmp_amount[110] = {0};
zxerr_t zxerr = bytesAmountToStringBalance(amount, sizeof(amount), tmp_amount, sizeof(tmp_amount));

if (zxerr != zxerr_ok || strnlen(tmp_amount, sizeof(tmp_amount)) > sizeof(params->printable_amount)) {
return;
}
strncpy(params->printable_amount, tmp_amount, sizeof(params->printable_amount) - 1);
}
165 changes: 165 additions & 0 deletions app/src/swap/handle_sign_transaction.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*******************************************************************************
* (c) 2016 Ledger
* (c) 2018 - 2023 Zondax AG
*
* 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.
********************************************************************************/
#include "bignum.h"
#include "crypto.h"
#include "lib_standard_app/swap_lib_calls.h"
#include "parser.h"
#include "swap.h"
#include "swap_utils.h"
#include "zxformat.h"

swap_globals_t G_swap_state;

// Save the BSS address where we will write the return value when finished
static uint8_t *G_swap_sign_return_value_address;

bool copy_transaction_parameters(create_transaction_parameters_t *sign_transaction_params) {
if (sign_transaction_params == NULL) {
return false;
}

// First copy parameters to stack, and then to global data.
// We need this "trick" as the input data position can overlap with globals
char destination_address[ADDRESS_MAXSIZE] = {0};
uint8_t amount[COIN_AMOUNT_MAXSIZE] = {0};
uint8_t amount_length = {0};
uint8_t fees[COIN_AMOUNT_MAXSIZE] = {0};
uint8_t fees_length = {0};
char memo[MEMO_MAXSIZE] = {0};

// Check and copy destination address
if ((destination_address[sizeof(destination_address) - 1] != '\0') || (sign_transaction_params->amount_length > COIN_AMOUNT_MAXSIZE) ||
(sign_transaction_params->fee_amount_length > COIN_AMOUNT_MAXSIZE)) {
return false;
}
strncpy(destination_address, sign_transaction_params->destination_address, sizeof(destination_address));

// Check and copy memo
if(strlen(sign_transaction_params->destination_address_extra_id) >= sizeof(G_swap_state.memo)) {
return false;
}
strncpy(memo, sign_transaction_params->destination_address_extra_id, sizeof(G_swap_state.memo));

// Check and copy amount
memcpy(amount, sign_transaction_params->amount, sign_transaction_params->amount_length);
amount_length = sign_transaction_params->amount_length;

// Check and copy fees
memcpy(fees, sign_transaction_params->fee_amount, sign_transaction_params->fee_amount_length);
fees_length = sign_transaction_params->fee_amount_length;

// Full reset the global variables
os_explicit_zero_BSS_segment();

// Keep the address at which we'll reply the signing status
G_swap_sign_return_value_address = &sign_transaction_params->result;

// Commit the values read from exchange to the clean global space
G_swap_state.amount_length = amount_length;
memcpy(G_swap_state.amount, amount, sizeof(amount));
G_swap_state.fees_length = fees_length;
memcpy(G_swap_state.fees, fees, sizeof(G_swap_state.fees));
memcpy(G_swap_state.destination_address, destination_address, sizeof(G_swap_state.destination_address));
memcpy(G_swap_state.memo, memo, sizeof(G_swap_state.memo));

return true;
}

// Ensure the received transaction matches what was validated in the Exchange app UI
// at this poin, transaction was parsed by the app,so we need to compare what we aprsed with what is saved in the global state
parser_error_t check_swap_conditions(parser_context_t *ctx_parsed_tx) {
parser_error_t err = parser_unexpected_error;
if (ctx_parsed_tx == NULL) {
return err;
}

uint8_t displayIdx = 0;
uint8_t pageIdx = 0;
uint8_t pageCount = 0;
char tmpKey[20] = {0};
char tmpValue[65] = {0};

// Cosmos App in normal mode requires that chain id is the default one. If not, it will print expert mode fields
// this means if we reach this point and no chain_id is printed, chain_id must be the default one
const char *default_chain_id = "cosmoshub-4";
CHECK_PARSER_ERR(parser_getItem(ctx_parsed_tx, displayIdx, tmpKey, sizeof(tmpKey), tmpValue, sizeof(tmpValue), pageIdx, &pageCount))

// Check if chain_id is printed, expert fields
if (strcmp(tmpKey, "Chain ID") == 0) {
// For now allow only default chain id
if (strcmp(tmpValue, default_chain_id) != 0) {
ZEMU_LOGF(200, "Wrong Chain Id. ('%s', should be : '%s').\n", tmpValue, default_chain_id);
return parser_unexpected_error;
}
displayIdx += 5; // skip account_number, sequence, source_address, source_coins
} else {
displayIdx += 2; // skipsource_address, source_coins
}

// Check destination address
CHECK_PARSER_ERR(parser_getItem(ctx_parsed_tx, displayIdx, tmpKey, sizeof(tmpKey), tmpValue, sizeof(tmpValue), pageIdx, &pageCount))
if (strcmp(tmpKey, "Dest Address") != 0 || strcmp(tmpValue, G_swap_state.destination_address) != 0) {
ZEMU_LOGF(200, "Wrong swap tx destination address ('%s', should be : '%s').\n", tmpValue, G_swap_state.destination_address);
return parser_unexpected_error;
}

// Check destination coins
char tmp_amount[100] = {0};
zxerr_t zxerr = bytesAmountToStringBalance(G_swap_state.amount, G_swap_state.amount_length, tmp_amount, sizeof(tmp_amount));
if (zxerr != zxerr_ok) {
return parser_unexpected_error;
}

displayIdx += 1;
CHECK_PARSER_ERR(parser_getItem(ctx_parsed_tx, displayIdx, tmpKey, sizeof(tmpKey), tmpValue, sizeof(tmpValue), pageIdx, &pageCount))
if (strcmp(tmpKey, "Dest Coins") != 0 || strcmp(tmp_amount, tmpValue) != 0) {
ZEMU_LOGF(200, "Wrong swap tx destination coins ('%s', should be : '%s').\n", tmpValue, tmp_amount);
return parser_unexpected_error;
}

// Check if memo is present
displayIdx += 1;
CHECK_PARSER_ERR(parser_getItem(ctx_parsed_tx, displayIdx, tmpKey, sizeof(tmpKey), tmpValue, sizeof(tmpValue), pageIdx, &pageCount))
if (strcmp(tmpKey, "Memo") == 0) {
if(strcmp(tmpValue, G_swap_state.memo) != 0) {
ZEMU_LOGF(200, "Wrong swap tx memo ('%s', should be : '%s').\n", tmpValue, G_swap_state.memo);
return parser_unexpected_error;
}

// Prepare for fees
displayIdx += 1;
CHECK_PARSER_ERR(parser_getItem(ctx_parsed_tx, displayIdx, tmpKey, sizeof(tmpKey), tmpValue, sizeof(tmpValue), pageIdx, &pageCount))
}

//Check fees
zxerr = bytesAmountToStringBalance(G_swap_state.fees, G_swap_state.fees_length, tmp_amount, sizeof(tmp_amount));
if (zxerr != zxerr_ok) {
return parser_unexpected_error;
}

if (strcmp(tmpKey, "Fee") != 0 || strcmp(tmp_amount, tmpValue) != 0) {
ZEMU_LOGF(200, "Wrong swap tx fees ('%s', should be : '%s').\n", tmpValue, tmp_amount);
return parser_unexpected_error;
}

return parser_ok;
}

void __attribute__((noreturn)) finalize_exchange_sign_transaction(bool is_success) {
*G_swap_sign_return_value_address = is_success;
os_lib_end();
}
Loading

0 comments on commit 7039892

Please sign in to comment.