diff --git a/Makefile b/Makefile index 1b086394..41b77d3d 100644 --- a/Makefile +++ b/Makefile @@ -179,7 +179,7 @@ APPNAME ="Resistance" APP_LOAD_PARAMS += --path $(APP_PATH) else ifeq ($(COIN),ravencoin) # Ravencoin -DEFINES += BIP44_COIN_TYPE=175 BIP44_COIN_TYPE_2=175 COIN_P2PKH_VERSION=60 COIN_P2SH_VERSION=122 COIN_FAMILY=1 COIN_COINID=\"Ravencoin\" COIN_COINID_HEADER=\"RAVENCOIN\" COIN_COLOR_HDR=0x2E4A80 COIN_COLOR_DB=0x74829E COIN_COINID_NAME=\"Ravencoin\" COIN_COINID_SHORT=\"RVN\" COIN_KIND=COIN_KIND_RAVENCOIN +DEFINES += BIP44_COIN_TYPE=175 BIP44_COIN_TYPE_2=175 COIN_P2PKH_VERSION=60 COIN_P2SH_VERSION=122 COIN_FAMILY=1 COIN_COINID=\"Raven\" COIN_COINID_HEADER=\"RAVENCOIN\" COIN_COLOR_HDR=0x2E4A80 COIN_COLOR_DB=0x74829E COIN_COINID_NAME=\"Ravencoin\" COIN_COINID_SHORT=\"RVN\" COIN_KIND=COIN_KIND_RAVENCOIN APPNAME ="Ravencoin" APP_LOAD_PARAMS += --path $(APP_PATH) else diff --git a/include/btchip_context.h b/include/btchip_context.h index a80f44bb..cd214c4f 100644 --- a/include/btchip_context.h +++ b/include/btchip_context.h @@ -24,7 +24,7 @@ #include "btchip_secure_value.h" #include "btchip_filesystem_tx.h" -#define MAX_OUTPUT_TO_CHECK 100 +#define MAX_OUTPUT_TO_CHECK 120 //Ravencoin asset max #define MAX_COIN_ID 13 #define MAX_SHORT_COIN_ID 5 diff --git a/include/btchip_helpers.h b/include/btchip_helpers.h index a99504b7..02941397 100644 --- a/include/btchip_helpers.h +++ b/include/btchip_helpers.h @@ -31,7 +31,9 @@ #define OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET 3 unsigned char btchip_output_script_is_regular(unsigned char *buffer); +unsigned char btchip_output_script_is_regular_ravencoin_asset(unsigned char *buffer); unsigned char btchip_output_script_is_p2sh(unsigned char *buffer); +unsigned char btchip_output_script_is_p2sh_ravencoin_asset(unsigned char *buffer); unsigned char btchip_output_script_is_op_return(unsigned char *buffer); unsigned char btchip_output_script_is_native_witness(unsigned char *buffer); @@ -40,6 +42,9 @@ unsigned char btchip_output_script_is_op_create(unsigned char *buffer, unsigned char btchip_output_script_is_op_call(unsigned char *buffer, size_t size); +signed char btchip_output_script_try_get_ravencoin_asset_tag_type(unsigned char *buffer); +unsigned char btchip_output_script_get_ravencoin_asset_ptr(unsigned char *buffer, size_t size, int *ptr); + void btchip_sleep16(unsigned short delay); void btchip_sleep32(unsigned long int delayEach, unsigned long int delayRepeat); @@ -80,6 +85,8 @@ unsigned char bip32_print_path(unsigned char *bip32Path, char* out, unsigned cha #define btchip_set_check_internal_structure_integrity(x) void btchip_swap_bytes(unsigned char *target, unsigned char *source, unsigned char size); +void btchip_swap_bytes_reversed(unsigned char *target, unsigned char *source, + unsigned char size); void btchip_sign_finalhash(void *keyContext, unsigned char *in, unsigned short inlen, diff --git a/src/btchip_apdu_hash_input_finalize_full.c b/src/btchip_apdu_hash_input_finalize_full.c index 5831b25d..ff2c15fc 100644 --- a/src/btchip_apdu_hash_input_finalize_full.c +++ b/src/btchip_apdu_hash_input_finalize_full.c @@ -42,6 +42,8 @@ void btchip_apdu_hash_input_finalize_full_reset(void) { static bool check_output_displayable() { bool displayable = true; + bool invalid_script = false; + int dummy; unsigned char amount[8], isOpReturn, isP2sh, isNativeSegwit, j, nullAmount = 1; unsigned char isOpCreate, isOpCall; @@ -52,14 +54,23 @@ static bool check_output_displayable() { break; } } + if (!nullAmount) { btchip_swap_bytes(amount, btchip_context_D.currentOutput, 8); transaction_amount_add_be(btchip_context_D.totalOutputAmount, btchip_context_D.totalOutputAmount, amount); } + isOpReturn = btchip_output_script_is_op_return(btchip_context_D.currentOutput + 8); - isP2sh = btchip_output_script_is_p2sh(btchip_context_D.currentOutput + 8); + + if (nullAmount && G_coin_config->kind==COIN_KIND_RAVENCOIN){ + isP2sh = btchip_output_script_is_p2sh_ravencoin_asset(btchip_context_D.currentOutput + 8); + } + else { + isP2sh = btchip_output_script_is_p2sh(btchip_context_D.currentOutput + 8); + } + isNativeSegwit = btchip_output_script_is_native_witness( btchip_context_D.currentOutput + 8); isOpCreate = @@ -68,13 +79,45 @@ static bool check_output_displayable() { isOpCall = btchip_output_script_is_op_call(btchip_context_D.currentOutput + 8, sizeof(btchip_context_D.currentOutput) - 8); - if (((G_coin_config->kind == COIN_KIND_QTUM) && - !btchip_output_script_is_regular(btchip_context_D.currentOutput + 8) && - !isP2sh && !(nullAmount && isOpReturn) && !isOpCreate && !isOpCall) || - (!(G_coin_config->kind == COIN_KIND_QTUM) && - !btchip_output_script_is_regular(btchip_context_D.currentOutput + 8) && - !isP2sh && !(nullAmount && isOpReturn))) { - PRINTF("Error : Unrecognized output script"); + + if (G_coin_config->kind == COIN_KIND_RAVENCOIN) { + PRINTF("Asset script value: %d\n", btchip_output_script_get_ravencoin_asset_ptr( + btchip_context_D.currentOutput + 8, + sizeof(btchip_context_D.currentOutput) - 8, + &dummy + )); + PRINTF("Null asset script value: %d\n", btchip_output_script_try_get_ravencoin_asset_tag_type(btchip_context_D.currentOutput + 8)); + PRINTF("Null amount: %d\n", nullAmount); + } + + if (G_coin_config->kind == COIN_KIND_QTUM) { + invalid_script = + !btchip_output_script_is_regular(btchip_context_D.currentOutput + 8) && + !isP2sh && !(nullAmount && isOpReturn) && !isOpCreate && !isOpCall; + } + else if (nullAmount && G_coin_config->kind == COIN_KIND_RAVENCOIN) { + // Ravencoin assets only come into play when there is a null amount + invalid_script = + (!btchip_output_script_get_ravencoin_asset_ptr( + btchip_context_D.currentOutput + 8, + sizeof(btchip_context_D.currentOutput) - 8, + &dummy) + || + (!btchip_output_script_is_regular_ravencoin_asset(btchip_context_D.currentOutput + 8) + && + !isP2sh + ) + ) + && !isOpReturn + && -1 == btchip_output_script_try_get_ravencoin_asset_tag_type(btchip_context_D.currentOutput + 8); + } + else { + invalid_script = + !btchip_output_script_is_regular(btchip_context_D.currentOutput + 8) && + !isP2sh && !(nullAmount && isOpReturn); + } + if (invalid_script) { + PRINTF("Error : Unrecognized output script\n"); THROW(EXCEPTION); } if (btchip_context_D.tmpCtx.output.changeInitialized && !isOpReturn) { diff --git a/src/btchip_display_variables.h b/src/btchip_display_variables.h index f62263cb..d6ea3f1c 100644 --- a/src/btchip_display_variables.h +++ b/src/btchip_display_variables.h @@ -22,9 +22,10 @@ union display_variables { struct { // char addressSummary[40]; // beginning of the output address ... end // of - char fullAddress[65]; // the address - char fullAmount[20]; // full amount + //Ravencoin: max asset length (32) + space (1) + max amt whole (11) + decimal (1) + max amt decimal (8) + \0 (1) + char fullAmount[54]; + //char fullAmount[20]; // full amount char feesAmount[20]; // fees } tmp; diff --git a/src/btchip_helpers.c b/src/btchip_helpers.c index a7131b0c..8cd8b351 100644 --- a/src/btchip_helpers.c +++ b/src/btchip_helpers.c @@ -59,6 +59,7 @@ const unsigned char ZEN_OUTPUT_SCRIPT_POST[] = { }; // BIP0115 Replay Protection unsigned char btchip_output_script_is_regular(unsigned char *buffer) { + int i; if (G_coin_config->native_segwit_prefix) { if ((os_memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE)) == 0) || @@ -75,7 +76,8 @@ unsigned char btchip_output_script_is_regular(unsigned char *buffer) { sizeof(ZEN_OUTPUT_SCRIPT_POST)) == 0)) { return 1; } - } else { + } + else { if ((os_memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_PRE, sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE)) == 0) && (os_memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE) + 20, @@ -87,6 +89,19 @@ unsigned char btchip_output_script_is_regular(unsigned char *buffer) { return 0; } +unsigned char btchip_output_script_is_regular_ravencoin_asset(unsigned char *buffer) { + if (G_coin_config->kind == COIN_KIND_RAVENCOIN) { + if ((os_memcmp(buffer + 1, TRANSACTION_OUTPUT_SCRIPT_PRE + 1, + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE) - 1) == 0) && + (os_memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE) + 20, + TRANSACTION_OUTPUT_SCRIPT_POST, + sizeof(TRANSACTION_OUTPUT_SCRIPT_POST)) == 0)) { + return 1; + } + } + return 0; +} + unsigned char btchip_output_script_is_p2sh(unsigned char *buffer) { if (G_coin_config->kind == COIN_KIND_HORIZEN) { if ((os_memcmp(buffer, ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE, @@ -108,6 +123,19 @@ unsigned char btchip_output_script_is_p2sh(unsigned char *buffer) { return 0; } +unsigned char btchip_output_script_is_p2sh_ravencoin_asset(unsigned char *buffer) { + if (G_coin_config->kind == COIN_KIND_RAVENCOIN) { + if ((os_memcmp(buffer + 1, TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE + 1, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) - 1) == 0) && + (os_memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) + 20, + TRANSACTION_OUTPUT_SCRIPT_P2SH_POST, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_POST)) == 0)) { + return 1; + } + } + return 0; +} + unsigned char btchip_output_script_is_native_witness(unsigned char *buffer) { if (G_coin_config->native_segwit_prefix) { if ((os_memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, @@ -149,6 +177,90 @@ unsigned char btchip_output_script_is_op_call(unsigned char *buffer, return output_script_is_op_create_or_call(buffer, size, 0xC2); } +signed char btchip_output_script_try_get_ravencoin_asset_tag_type(unsigned char *buffer) { + if (btchip_output_script_is_regular(buffer) || + btchip_output_script_is_p2sh(buffer) || + btchip_output_script_is_op_return(buffer) || + (buffer[1] != 0xC0)) { + return -1; + } + if (buffer[2] == 0x50) { + if (buffer[3] == 0x50) { + return 2; + } + return 1; + } + return 0; +} + +unsigned char btchip_output_script_get_ravencoin_asset_ptr(unsigned char *buffer, size_t size, int *ptr) { + // This method is also used in check_output_displayable and needs to ensure no overflows happen from bad scripts + unsigned int script_ptr = 1; // Skip the first pushdata op + unsigned int op = -1; + unsigned int final_op = buffer[0]; + unsigned char asset_len; + + if (final_op >= size || buffer[final_op] != 0x75) { + PRINTF("Ravencoin pointer quick return\n"); + return 0; + } + while (script_ptr < final_op - 7) { // Definitely a bad asset script; too short + op = buffer[script_ptr++]; + if (op == 0xC0) { + // Verifying script + if ((buffer[script_ptr+1] == 0x72) && + (buffer[script_ptr+2] == 0x76) && + (buffer[script_ptr+3] == 0x6E)) { + asset_len = buffer[script_ptr+5]; + if (asset_len > 32) { // Invalid script + return 0; + } + if (buffer[script_ptr+4] == 0x6F) { + // Ownership assets will not have an amount + if (script_ptr+5+asset_len >= final_op) { + return 0; // Too small + } + } + else { + if (script_ptr+5+asset_len+8 >= final_op) { + return 0; // Too small + } + } + *ptr = script_ptr + 4; + } else { + asset_len = buffer[script_ptr+6]; + if (asset_len > 32) { // Invalid script + return 0; + } + if (buffer[script_ptr+5] == 0x6F) { + // Ownership assets will not have an amount + if (script_ptr+6+asset_len >= final_op) { + return 0; // Too small + } + } + else { + if (script_ptr+6+asset_len+8 >= final_op) { + return 0; // Too small + } + } + *ptr = script_ptr + 5; + } + return 1; + } + else if (op <= 0x4E) { + if (op < 0x4C) { + script_ptr += op; + } + else { + script_ptr += (buffer[script_ptr] + 1); + } + //There shouldn't be anything pushed larger than 256 bytes in an asset transfer script + } + } + PRINTF("Ravencoin pointer end\n"); + return 0; +} + unsigned char btchip_rng_u8_modulo(unsigned char modulo) { unsigned int rng_max = 256 % modulo; unsigned int rng_limit = 256 - rng_max; @@ -265,6 +377,14 @@ void btchip_swap_bytes(unsigned char *target, unsigned char *source, } } +void btchip_swap_bytes_reversed(unsigned char *target, unsigned char *source, + unsigned char size) { + unsigned char i; + for (i = 0; i < size; i++) { + target[i] = source[i]; + } +} + unsigned short btchip_decode_base58_address(unsigned char *in, unsigned short inlen, unsigned char *out, diff --git a/src/main.c b/src/main.c index e8ae7f50..c248e92d 100644 --- a/src/main.c +++ b/src/main.c @@ -841,7 +841,12 @@ uint8_t prepare_fees() { #define MAIDSAFE_ASSETID 3 #define USDT_ASSETID 31 +#define RAVENCOIN_NULL_ASSET_TAG 0 +#define RAVENCOIN_NULL_ASSET_VERIFIER 1 +#define RAVENCOIN_NULL_ASSET_RESTRICTED 2 + void get_address_from_output_script(unsigned char* script, int script_size, char* out, int out_size) { + if (btchip_output_script_is_op_return(script)) { strcpy(out, "OP_RETURN"); return; @@ -856,6 +861,19 @@ void get_address_from_output_script(unsigned char* script, int script_size, char strcpy(out, "OP_CALL"); return; } + if ((G_coin_config->kind == COIN_KIND_RAVENCOIN)) { + switch(btchip_output_script_try_get_ravencoin_asset_tag_type(script)) { + case RAVENCOIN_NULL_ASSET_TAG: + strcpy(out, "ASSET TAG"); + return; + case RAVENCOIN_NULL_ASSET_VERIFIER: + strcpy(out, "ASSET VERIFIER"); + return; + case RAVENCOIN_NULL_ASSET_RESTRICTED: + strcpy(out, "ASSET RESTRICTED"); + return; + } + } if (btchip_output_script_is_native_witness(script)) { if (G_coin_config->native_segwit_prefix) { segwit_addr_encode( @@ -870,8 +888,14 @@ void get_address_from_output_script(unsigned char* script, int script_size, char unsigned short textSize; int addressOffset = 3; unsigned short version = G_coin_config->p2sh_version; + unsigned int dummy; - if (btchip_output_script_is_regular(script)) { + if (G_coin_config->kind == COIN_KIND_RAVENCOIN + && btchip_output_script_get_ravencoin_asset_ptr(script, script_size, &dummy) + && btchip_output_script_is_regular_ravencoin_asset(script)) { + addressOffset = 4; + version = G_coin_config->p2pkh_version; + } else if (btchip_output_script_is_regular(script)) { addressOffset = 4; version = G_coin_config->p2pkh_version; } @@ -907,6 +931,8 @@ uint8_t prepare_single_output() { unsigned int offset = 0; unsigned short textSize; char tmp[80] = {0}; + int ravencoin_asset_ptr = -1; + unsigned char str_len; btchip_swap_bytes(amount, btchip_context_D.currentOutput + offset, 8); offset += 8; @@ -942,15 +968,42 @@ uint8_t prepare_single_output() { vars.tmp.fullAmount[textSize + headerLength] = '\0'; } else { - os_memmove(vars.tmp.fullAmount, G_coin_config->name_short, - strlen(G_coin_config->name_short)); - vars.tmp.fullAmount[strlen(G_coin_config->name_short)] = ' '; - btchip_context_D.tmp = - (unsigned char *)(vars.tmp.fullAmount + - strlen(G_coin_config->name_short) + 1); + if ((G_coin_config->kind == COIN_KIND_RAVENCOIN) && + (btchip_output_script_get_ravencoin_asset_ptr(btchip_context_D.currentOutput + offset, \ + sizeof(btchip_context_D.currentOutput) - offset, \ + &ravencoin_asset_ptr))) { + unsigned char type; + unsigned char one_in_sats[8] = {0x00, 0xE1, 0xF5, 0x05, 0x00, 0x00, 0x00, 0x00}; + type = (btchip_context_D.currentOutput + offset)[ravencoin_asset_ptr++]; + str_len = (btchip_context_D.currentOutput + offset)[ravencoin_asset_ptr++]; + btchip_swap_bytes_reversed(vars.tmp.fullAmount, btchip_context_D.currentOutput + offset + ravencoin_asset_ptr, str_len); + ravencoin_asset_ptr += str_len; + vars.tmp.fullAmount[str_len] = ' '; + btchip_context_D.tmp = + (unsigned char *)(vars.tmp.fullAmount + + str_len + 1); + if (type == 0x6F) { + // Ownership amounts do not have an associated amount; give it 100,000,000 virtual sats + btchip_swap_bytes(amount, one_in_sats, 8); + } + else { + btchip_swap_bytes(amount, btchip_context_D.currentOutput + offset + ravencoin_asset_ptr, 8); + } + } + else { + PRINTF("Normal out parsing\n"); + str_len = strlen(G_coin_config->name_short); + os_memmove(vars.tmp.fullAmount, G_coin_config->name_short, + str_len); + vars.tmp.fullAmount[str_len] = ' '; + btchip_context_D.tmp = + (unsigned char *)(vars.tmp.fullAmount + + str_len + 1); + } textSize = btchip_convert_hex_amount_to_displayable(amount); - vars.tmp.fullAmount[textSize + strlen(G_coin_config->name_short) + 1] = + vars.tmp.fullAmount[textSize + str_len + 1] = '\0'; + } return 1;