Browse Source

Add Bitcoin Private, ZenCash, CashAddr support

zcash_overwinter app-125
BTChip github 7 years ago
parent
commit
74f61cf101
No known key found for this signature in database GPG Key ID: 48BCF826EBFA4D17
  1. 23
      Makefile
  2. BIN
      glyphs/nanos_badge_bitcoin_private.gif
  3. BIN
      glyphs/nanos_badge_zencash.gif
  4. 6
      include/btchip_context.h
  5. BIN
      nanos_app_bitcoin_private.gif
  6. BIN
      nanos_app_zencash.gif
  7. 21
      src/btchip_apdu_get_wallet_public_key.c
  8. 8
      src/btchip_apdu_hash_input_finalize_full.c
  9. 12
      src/btchip_apdu_hash_input_start.c
  10. 2
      src/btchip_apdu_set_alternate_coin_version.c
  11. 35
      src/btchip_helpers.c
  12. 153
      src/cashaddr.c
  13. 48
      src/cashaddr.h
  14. 48
      src/main.c

23
Makefile

@ -28,7 +28,7 @@ APP_LOAD_PARAMS= --curve secp256k1 $(COMMON_LOAD_PARAMS)
APPVERSION_M=1
APPVERSION_N=2
APPVERSION_P=4
APPVERSION_P=5
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
# simplify for tests
@ -80,6 +80,11 @@ else ifeq ($(COIN),zcash)
DEFINES += COIN_P2PKH_VERSION=7352 COIN_P2SH_VERSION=7357 COIN_FAMILY=1 COIN_COINID=\"Zcash\" COIN_COINID_HEADER=\"ZCASH\" COIN_COLOR_HDR=0x3790CA COIN_COLOR_DB=0x9BC8E5 COIN_COINID_NAME=\"Zcash\" COIN_COINID_SHORT=\"ZEC\" COIN_KIND=COIN_KIND_ZCASH
APPNAME ="Zcash"
APP_LOAD_PARAMS += --path $(APP_PATH)
else ifeq ($(COIN),zencash)
# ZenCash
DEFINES += COIN_P2PKH_VERSION=8329 COIN_P2SH_VERSION=8342 COIN_FAMILY=4 COIN_COINID=\"Zencash\" COINID_UPCASE=\"ZENCASH\" COLOR_HDR=0xFF4300 COLOR_DB=0xFF8356 COIN_COINID_NAME=\"Zencash\" COINID=$(COIN) COIN_COINID_SHORT=\"ZEN\" COIN_KIND=COIN_KIND_ZENCASH
APPNAME ="ZenCash"
APP_LOAD_PARAMS += --path $(APP_PATH)
else ifeq ($(COIN),komodo)
# Komodo
DEFINES += COIN_P2PKH_VERSION=60 COIN_P2SH_VERSION=85 COIN_FAMILY=1 COIN_COINID=\"Komodo\" COIN_COINID_HEADER=\"KMD\" COIN_COLOR_HDR=0x326464 COIN_COLOR_DB=0x99b2b2 COIN_COINID_NAME=\"Komodo\" COIN_COINID_SHORT=\"KMD\" COIN_KIND=COIN_KIND_KOMODO
@ -125,16 +130,22 @@ APPNAME ="Digibyte"
APP_LOAD_PARAMS += --path $(APP_PATH)
else ifeq ($(COIN),qtum)
# Qtum
DEFINES += COIN_P2PKH_VERSION=58 COIN_P2SH_VERSION=50 COIN_FAMILY=3 COIN_COINID=\"Qtum\" COIN_COINID_HEADER=\"QTUM\" COIN_COLOR_HDR=0x2E9AD0 COIN_COLOR_DB=0x97CDE8 COIN_COINID_NAME=\"QTUM\" COIN_COINID_SHORT=\"QTUM\" COIN_KIND=COIN_KIND_QTUM COIN_FLAGS=FLAG_QTUM_SUPPORT
DEFINES += COIN_P2PKH_VERSION=58 COIN_P2SH_VERSION=50 COIN_FAMILY=3 COIN_COINID=\"Qtum\" COIN_COINID_HEADER=\"QTUM\" COIN_COLOR_HDR=0x2E9AD0 COIN_COLOR_DB=0x97CDE8 COIN_COINID_NAME=\"QTUM\" COIN_COINID_SHORT=\"QTUM\" COIN_KIND=COIN_KIND_QTUM
APPNAME ="Qtum"
APP_LOAD_PARAMS += --path "44'/88'" --path "0'/45342'" --path "20698'/3053'/12648430'"
else ifeq ($(COIN),hcash)
# HCash
DEFINES += COIN_P2PKH_VERSION=40 COIN_P2SH_VERSION=100 COIN_FAMILY=2 COIN_COINID=\"HShare\" COIN_COINID_HEADER=\"HCASH\" COIN_COLOR_HDR=0x57448D COIN_COLOR_DB=0xABA2C6 COIN_COINID_NAME=\"HCash\" COIN_COINID_SHORT=\"HCASH\" COIN_KIND=COIN_KIND_HCASH COIN_FLAGS=FLAG_PEERCOIN_SUPPORT
APPNAME ="HCash"
APP_LOAD_PARAMS += --path $(APP_PATH)
else ifeq ($(COIN),bitcoin_private)
# Bitcoin Private
DEFINES += COIN_P2PKH_VERSION=4901 COIN_P2SH_VERSION=5039 COIN_FAMILY=1 COIN_COINID=\"BPrivate\" COIN_COINID_HEADER=\"BPRIVATE\" COIN_COLOR_HDR=0x85bb65 COIN_COLOR_DB=0xc2ddb2 COIN_COINID_NAME=\"BPrivate\" COIN_COINID_SHORT=\"BTCP\" COIN_KIND=COIN_KIND_BITCOIN_PRIVATE COIN_FORKID=42
APPNAME ="Bitcoin Private"
APP_LOAD_PARAMS += --path $(APP_PATH)
else
ifeq ($(filter clean,$(MAKECMDGOALS)),)
$(error Unsupported COIN - use bitcoin_testnet, bitcoin, bitcoin_cash, bitcoin_gold, litecoin, dogecoin, dash, zcash, komodo, stratis, peercoin, posw, pivx, viacoin, vertcoin, stealthcoin, digibyte, qtum, hcash)
$(error Unsupported COIN - use bitcoin_testnet, bitcoin, bitcoin_cash, bitcoin_gold, litecoin, dogecoin, dash, zcash, zencash, komodo, stratis, peercoin, posw, pivx, viacoin, vertcoin, stealthcoin, digibyte, qtum, hcash, bitcoin_private)
endif
endif
@ -159,14 +170,16 @@ all: default
DEFINES += OS_IO_SEPROXYHAL IO_SEPROXYHAL_BUFFER_SIZE_B=300
DEFINES += HAVE_BAGL HAVE_SPRINTF
#DEFINES += HAVE_PRINTF PRINTF=screen_printf
DEFINES += PRINTF\(...\)=
DEFINES += HAVE_PRINTF PRINTF=screen_printf
#DEFINES += PRINTF\(...\)=
DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=6 IO_HID_EP_LENGTH=64 HAVE_USB_APDU
DEFINES += LEDGER_MAJOR_VERSION=$(APPVERSION_M) LEDGER_MINOR_VERSION=$(APPVERSION_N) LEDGER_PATCH_VERSION=$(APPVERSION_P) TCS_LOADER_PATCH_VERSION=0
DEFINES += UNUSED\(x\)=\(void\)x
DEFINES += APPVERSION=\"$(APPVERSION)\"
DEFINES += CX_COMPLIANCE_141
##############
# Compiler #
##############

BIN
glyphs/nanos_badge_bitcoin_private.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
glyphs/nanos_badge_zencash.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

6
include/btchip_context.h

@ -183,6 +183,7 @@ struct btchip_context_s {
unsigned char transactionVersion[4];
unsigned char inputValue[8];
unsigned char usingSegwit;
unsigned char usingCashAddr;
unsigned char segwitParsedOnce;
/* /Segregated Witness changes */
@ -239,8 +240,7 @@ typedef struct btchip_context_s btchip_context_t;
typedef enum btchip_coin_flags_e {
FLAG_PEERCOIN_UNITS=1,
FLAG_PEERCOIN_SUPPORT=2,
FLAG_SEGWIT_CHANGE_SUPPORT=4,
FLAG_QTUM_SUPPORT=8,
FLAG_SEGWIT_CHANGE_SUPPORT=4
} btchip_coin_flags_t;
@ -265,6 +265,8 @@ typedef enum btchip_coin_kind_e {
COIN_KIND_DIGIBYTE,
COIN_KIND_QTUM,
COIN_KIND_HCASH,
COIN_KIND_BITCOIN_PRIVATE,
COIN_KIND_ZENCASH
} btchip_coin_kind_t;
typedef struct btchip_altcoin_config_s {

BIN
nanos_app_bitcoin_private.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
nanos_app_zencash.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 B

21
src/btchip_apdu_get_wallet_public_key.c

@ -21,12 +21,15 @@
#include "btchip_bagl_extensions.h"
#include "segwit_addr.h"
#include "cashaddr.h"
#define P1_NO_DISPLAY 0x00
#define P1_DISPLAY 0x01
#define P2_LEGACY 0x00
#define P2_SEGWIT 0x01
#define P2_NATIVE_SEGWIT 0x02
#define P2_CASHADDR 0x03
unsigned short btchip_apdu_get_wallet_public_key() {
unsigned char keyLength;
@ -37,6 +40,7 @@ unsigned short btchip_apdu_get_wallet_public_key() {
bool display = (G_io_apdu_buffer[ISO_OFFSET_P1] == P1_DISPLAY);
bool segwit = (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_SEGWIT);
bool nativeSegwit = (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NATIVE_SEGWIT);
bool cashAddr = (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_CASHADDR);
switch (G_io_apdu_buffer[ISO_OFFSET_P1]) {
case P1_NO_DISPLAY:
@ -54,6 +58,11 @@ unsigned short btchip_apdu_get_wallet_public_key() {
case P2_LEGACY:
case P2_SEGWIT:
break;
case P2_CASHADDR:
if (G_coin_config->kind != COIN_KIND_BITCOIN_CASH) {
return BTCHIP_SW_INCORRECT_P1_P2;
}
break;
default:
return BTCHIP_SW_INCORRECT_P1_P2;
}
@ -78,7 +87,10 @@ unsigned short btchip_apdu_get_wallet_public_key() {
return BTCHIP_SW_SECURITY_STATUS_NOT_SATISFIED;
}
PRINTF("pin ok\n");
btchip_private_derive_keypair(keyPath, 1, chainCode);
G_io_apdu_buffer[0] = 65;
// Then encode it
@ -91,7 +103,14 @@ unsigned short btchip_apdu_get_wallet_public_key() {
os_memmove(G_io_apdu_buffer + 1, btchip_public_key_D.W,
sizeof(btchip_public_key_D.W));
if (!(segwit || nativeSegwit)) {
if (cashAddr) {
uint8_t tmp[20];
btchip_public_key_hash160(G_io_apdu_buffer + 1, // IN
keyLength, // INLEN
tmp);
keyLength =
cashaddr_encode(tmp, 20, G_io_apdu_buffer + 67, 50, CASHADDR_P2PKH);
} else if (!(segwit || nativeSegwit)) {
keyLength = btchip_public_key_to_encoded_base58(
G_io_apdu_buffer + 1, // IN
keyLength, // INLEN

8
src/btchip_apdu_hash_input_finalize_full.c

@ -26,6 +26,8 @@
#define FINALIZE_P1_LAST 0x80
#define FINALIZE_P1_CHANGEINFO 0xFF
#define FINALIZE_P2_DEFAULT 0x00
#define FLAG_SIGNATURE 0x01
#define FLAG_CHANGE_VALIDATED 0x80
@ -66,10 +68,10 @@ static bool check_output_displayable() {
btchip_output_script_is_op_create(btchip_context_D.currentOutput + 8);
isOpCall =
btchip_output_script_is_op_call(btchip_context_D.currentOutput + 8);
if (((G_coin_config->flags & FLAG_QTUM_SUPPORT) &&
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->flags & FLAG_QTUM_SUPPORT) &&
(!(G_coin_config->kind == COIN_KIND_QTUM) &&
!btchip_output_script_is_regular(btchip_context_D.currentOutput + 8) &&
!isP2sh && !(nullAmount && isOpReturn))) {
PRINTF("Error : Unrecognized input script");
@ -326,7 +328,7 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
btchip_context_D.currentOutputOffset += apduLength;
// Check if the legacy UI can be applied
if (!(G_coin_config->flags & FLAG_QTUM_SUPPORT) &&
if (!(G_coin_config->kind == COIN_KIND_QTUM) &&
(G_io_apdu_buffer[ISO_OFFSET_P1] == FINALIZE_P1_LAST) &&
!btchip_context_D.tmpCtx.output.multipleOutput &&
prepare_full_output(1)) {

12
src/btchip_apdu_hash_input_start.c

@ -22,6 +22,7 @@
#define P1_NEXT 0x80
#define P2_NEW 0x00
#define P2_NEW_SEGWIT 0x02
#define P2_NEW_SEGWIT_CASHADDR 0x03
#define P2_CONTINUE 0x80
unsigned short btchip_apdu_hash_input_start() {
@ -49,12 +50,16 @@ unsigned short btchip_apdu_hash_input_start() {
}
if ((G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW) ||
(G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT)) {
(G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT) ||
(G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_CASHADDR)) {
// btchip_context_D.transactionContext.consumeP2SH =
// ((N_btchip.bkp.config.options & BTCHIP_OPTION_SKIP_2FA_P2SH) != 0);
if (G_io_apdu_buffer[ISO_OFFSET_P1] == P1_FIRST) {
unsigned char usingSegwit =
(G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT);
(G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT) ||
((G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_CASHADDR));
unsigned char usingCashAddr =
(G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_CASHADDR);
// Request PIN validation
// Only request PIN validation (user presence) to start a new
// transaction signing flow.
@ -70,6 +75,9 @@ unsigned short btchip_apdu_hash_input_start() {
btchip_context_D.transactionContext.consumeP2SH = 0;
btchip_context_D.transactionContext.relaxed = 0;
btchip_context_D.usingSegwit = usingSegwit;
btchip_context_D.usingCashAddr =
(G_coin_config->kind == COIN_KIND_BITCOIN_CASH ? usingCashAddr
: 0);
btchip_context_D.segwitParsedOnce = 0;
btchip_set_check_internal_structure_integrity(1);
// Initialize for screen pairing

2
src/btchip_apdu_set_alternate_coin_version.c

@ -59,7 +59,7 @@ unsigned short btchip_apdu_set_alternate_coin_version() {
}
break;
case BTCHIP_FAMILY_QTUM:
if (!(G_coin_config->flags & FLAG_QTUM_SUPPORT)) {
if (!(G_coin_config->kind == COIN_KIND_QTUM)) {
goto incorrect_family;
}
break;

35
src/btchip_helpers.c

@ -31,6 +31,19 @@ const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2SH_POST[] = {0x87}; // OP_EQUAL
const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE[] = {0x16, 0x00, 0x14};
const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE[] = {0x22, 0x00, 0x20};
const unsigned char ZEN_OUTPUT_SCRIPT_PRE[] = {
0x3F, 0x76, 0xA9,
0x14}; // script length, OP_DUP, OP_HASH160, address length
const unsigned char ZEN_OUTPUT_SCRIPT_POST[] = {
0x88, 0xAC, // OP_EQUALVERIFY, OP_CHECKSIG
0x20, 0x9e, 0xc9, 0x84, 0x5a, 0xcb, 0x02, 0xfa, 0xb2, 0x4e, 0x1c, 0x03,
0x68, 0xb3, 0xb5, 0x17, 0xc1, 0xa4, 0x48, 0x8f, 0xba, 0x97, 0xf0, 0xe3,
0x45, 0x9a, 0xc0, 0x53, 0xea, 0x01, 0x00, 0x00, 0x00, // ParamHash
0x03, // Push 3 bytes to stack to make ParamHeight line up properly
0xc0, 0x1f, 0x02, // ParamHeight (139200) -> hex -> endianness swapped
0xb4 // OP_CHECKBLOCKATHEIGHT
}; // BIP0115 Replay Protection
unsigned char btchip_output_script_is_regular(unsigned char *buffer) {
if (G_coin_config->native_segwit_prefix) {
if ((os_memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE,
@ -40,12 +53,22 @@ unsigned char btchip_output_script_is_regular(unsigned char *buffer) {
return 1;
}
}
if ((os_memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_PRE,
sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE)) == 0) &&
(os_memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE) + 20,
TRANSACTION_OUTPUT_SCRIPT_POST,
sizeof(TRANSACTION_OUTPUT_SCRIPT_POST)) == 0)) {
return 1;
if (G_coin_config->kind == COIN_KIND_ZENCASH) {
if ((os_memcmp(buffer, ZEN_OUTPUT_SCRIPT_PRE,
sizeof(ZEN_OUTPUT_SCRIPT_PRE)) == 0) &&
(os_memcmp(buffer + sizeof(ZEN_OUTPUT_SCRIPT_PRE) + 20,
ZEN_OUTPUT_SCRIPT_POST,
sizeof(ZEN_OUTPUT_SCRIPT_POST)) == 0)) {
return 1;
}
} else {
if ((os_memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_PRE,
sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE)) == 0) &&
(os_memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE) + 20,
TRANSACTION_OUTPUT_SCRIPT_POST,
sizeof(TRANSACTION_OUTPUT_SCRIPT_POST)) == 0)) {
return 1;
}
}
return 0;
}

153
src/cashaddr.c

@ -0,0 +1,153 @@
/* Copyright (c) 2017 Pieter Wuille
* Modified work Copyright (c) 2018 Jonas Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "os.h"
#include "cx.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <inttypes.h>
#include "cashaddr.h"
static const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
uint64_t cashaddr_polymod_step(uint64_t pre) {
uint8_t b = pre >> 35;
return ((pre & 0x07ffffffff) << 5) ^ (-((b >> 0) & 1) & 0x98f2bc8e61UL) ^
(-((b >> 1) & 1) & 0x79b76d99e2UL) ^
(-((b >> 2) & 1) & 0xf33e5fb3c4UL) ^
(-((b >> 3) & 1) & 0xae2eabe2a8UL) ^
(-((b >> 4) & 1) & 0x1e4f43e470UL);
}
uint64_t PolyMod(uint8_t *prefix, uint8_t *payload, size_t payload_length) {
size_t i;
uint64_t c = 1;
while (*prefix != 0) {
c = cashaddr_polymod_step(c) ^ (*prefix++ & 0x1f); // Prefix
}
c = cashaddr_polymod_step(c); // The zero valued separator
for (i = 0; i < payload_length; ++i) {
c = cashaddr_polymod_step(c) ^ (*payload++); // Hash
}
for (i = 0; i < 8; ++i) {
c = cashaddr_polymod_step(c); // 8 zeros for empty checksum
}
return c ^ 1;
}
static int convert_bits(uint8_t *out, size_t *outlen, int outbits,
const uint8_t *in, size_t inlen, int inbits, int pad) {
uint32_t val = 0;
int bits = 0;
uint32_t maxv = (((uint32_t)1) << outbits) - 1;
while (inlen--) {
val = (val << inbits) | *(in++);
bits += inbits;
while (bits >= outbits) {
bits -= outbits;
out[(*outlen)++] = (val >> bits) & maxv;
}
}
if (pad) {
if (bits) {
out[(*outlen)++] = (val << (outbits - bits)) & maxv;
}
} else if (((val << (outbits - bits)) & maxv) || bits >= inbits) {
return 0;
}
return 1;
}
void create_checksum(uint8_t *payload, size_t payload_length,
uint8_t *checksum) {
uint8_t *prefix = (uint8_t *)"bitcoincash";
uint64_t mod = PolyMod(prefix, payload, payload_length);
for (size_t i = 0; i < 8; ++i) {
// Convert the 5-bit groups in mod to checksum values.
*checksum++ = (mod >> (5 * (7 - i))) & 0x1f;
}
}
int cashaddr_encode(uint8_t *hash, const size_t hash_length, uint8_t *addr,
const size_t max_addr_len, const unsigned short version) {
uint8_t version_byte;
uint8_t checksum[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // 5-bit bytes.
uint8_t
tmp[40]; // 8-bit bytes. Should be enough for 1 version byte + 160 bit
uint8_t payload[40]; // 5-bit bytes. Should be enough for 1 version byte +
// 160 bit hash
uint8_t *addr_start;
size_t payload_length = 0;
size_t addr_length = 0;
size_t i;
addr_start = addr;
*addr_start = 0;
if (hash_length != 20) // Only support 160 bit hash
return 0;
if (version == CASHADDR_P2PKH) { // Support P2PKH = 0, P2SH = 1
version_byte = 0;
} else if (version == CASHADDR_P2SH) {
version_byte = 8;
} else {
return 0;
}
tmp[0] = version_byte;
os_memmove(tmp + 1, hash, hash_length);
convert_bits(payload, &payload_length, 5, tmp, hash_length + 1, 8, 1);
create_checksum(payload, payload_length,
checksum); // Assume prefix is 'bitcoincash'
for (i = 0; i < payload_length; ++i) {
if (*payload >> 5) {
*addr_start = 0;
return 0;
}
addr_length++;
if (max_addr_len < addr_length) {
*addr_start = 0;
return 0;
}
*(addr++) = charset[payload[i]];
}
for (i = 0; i < 8; ++i) {
if (*checksum >> 5) {
*addr_start = 0;
return 0;
}
addr_length++;
if (max_addr_len < addr_length) {
*addr_start = 0;
return 0;
}
*(addr++) = charset[checksum[i]];
}
*addr = 0;
return addr_length;
}

48
src/cashaddr.h

@ -0,0 +1,48 @@
/* Copyright (c) 2017 Pieter Wuille
* Modified work Copyright (c) 2018 Jonas Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef _CASHADDR_H_
#define _CASHADDR_H_ 1
#include <stdint.h>
#define CASHADDR_P2PKH 0
#define CASHADDR_P2SH 1
/** Encode a Bitcoin Cash address
*
* In: hash: Pointer to the hash
* hash_length: Length of hash (bytes). Only 20 bytes (160 bits)
* supported.
* max_addr_len: Maximum length of encoded address
* version: P2PKH = 0, P2SH = 1
*
* Out: addr: Pointer to a buffer with encoded address. The encoded
* address end with a '\0'.
*
* Returns the length of the address if successful, 0 if unsuccessful.
*/
int cashaddr_encode(uint8_t *hash, const size_t hash_length, uint8_t *addr,
const size_t max_addr_len, const unsigned short version);
#endif

48
src/main.c

@ -26,6 +26,7 @@
#include "btchip_bagl_extensions.h"
#include "segwit_addr.h"
#include "cashaddr.h"
#include "glyphs.h"
@ -2002,11 +2003,11 @@ uint8_t prepare_single_output() {
if (btchip_output_script_is_op_return(btchip_context_D.currentOutput +
offset)) {
strcpy(vars.tmp.fullAddress, "OP_RETURN");
} else if ((G_coin_config->flags & FLAG_QTUM_SUPPORT) &&
} else if ((G_coin_config->kind == COIN_KIND_QTUM) &&
btchip_output_script_is_op_create(
btchip_context_D.currentOutput + offset)) {
strcpy(vars.tmp.fullAddress, "OP_CREATE");
} else if ((G_coin_config->flags & FLAG_QTUM_SUPPORT) &&
} else if ((G_coin_config->kind == COIN_KIND_QTUM) &&
btchip_output_script_is_op_call(btchip_context_D.currentOutput +
offset)) {
strcpy(vars.tmp.fullAddress, "OP_CALL");
@ -2034,10 +2035,18 @@ uint8_t prepare_single_output() {
btchip_context_D.currentOutput + addressOffset, 20);
// Prepare address
textSize = btchip_public_key_to_encoded_base58(
address, 20 + versionSize, (unsigned char *)tmp, sizeof(tmp),
version, 1);
tmp[textSize] = '\0';
if (btchip_context_D.usingCashAddr) {
cashaddr_encode(
address + versionSize, 20, tmp, sizeof(tmp),
(version == btchip_context_D.payToScriptHashVersion
? CASHADDR_P2SH
: CASHADDR_P2PKH));
} else {
textSize = btchip_public_key_to_encoded_base58(
address, 20 + versionSize, (unsigned char *)tmp,
sizeof(tmp), version, 1);
tmp[textSize] = '\0';
}
} else if (G_coin_config->native_segwit_prefix) {
textSize = segwit_addr_encode(
tmp, PIC(G_coin_config->native_segwit_prefix), 0,
@ -2124,7 +2133,7 @@ uint8_t prepare_full_output(uint8_t checkOnly) {
if (!btchip_output_script_is_regular(btchip_context_D.currentOutput +
offset) &&
!isP2sh && !(nullAmount && isOpReturn) &&
(!(G_coin_config->flags & FLAG_QTUM_SUPPORT) ||
(!(G_coin_config->kind == COIN_KIND_QTUM) ||
(!isOpCreate && !isOpCall))) {
if (!checkOnly) {
PRINTF("Error : Unrecognized input script");
@ -2138,10 +2147,10 @@ uint8_t prepare_full_output(uint8_t checkOnly) {
}
goto error;
}
if (((G_coin_config->flags & FLAG_QTUM_SUPPORT) &&
if (((G_coin_config->kind == COIN_KIND_QTUM) &&
btchip_context_D.tmpCtx.output.changeInitialized && !isOpReturn &&
!isOpCreate && !isOpCall) ||
(!(G_coin_config->flags & FLAG_QTUM_SUPPORT) &&
(!(G_coin_config->kind == COIN_KIND_QTUM) &&
btchip_context_D.tmpCtx.output.changeInitialized && !isOpReturn)) {
unsigned char addressOffset =
(isNativeSegwit ? OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET
@ -2185,14 +2194,14 @@ uint8_t prepare_full_output(uint8_t checkOnly) {
offset = 1;
btchip_context_D.tmp = (unsigned char *)tmp;
for (i = 0; i < numberOutputs; i++) {
if (((G_coin_config->flags & FLAG_QTUM_SUPPORT) &&
if (((G_coin_config->kind == COIN_KIND_QTUM) &&
!btchip_output_script_is_op_return(
btchip_context_D.currentOutput + offset + 8) &&
!btchip_output_script_is_op_create(
btchip_context_D.currentOutput + offset + 8) &&
!btchip_output_script_is_op_call(
btchip_context_D.currentOutput + offset + 8)) ||
(!(G_coin_config->flags & FLAG_QTUM_SUPPORT) &&
(!(G_coin_config->kind == COIN_KIND_QTUM) &&
!btchip_output_script_is_op_return(
btchip_context_D.currentOutput + offset + 8))) {
unsigned char versionSize;
@ -2230,10 +2239,19 @@ uint8_t prepare_full_output(uint8_t checkOnly) {
unsigned short textSize = 0;
if (!isNativeSegwit) {
// Prepare address
textSize = btchip_public_key_to_encoded_base58(
address, 20 + versionSize, (unsigned char *)tmp,
sizeof(tmp), version, 1);
tmp[textSize] = '\0';
if (btchip_context_D.usingCashAddr) {
cashaddr_encode(
address + versionSize, 20, tmp, sizeof(tmp),
(version ==
btchip_context_D.payToScriptHashVersion
? CASHADDR_P2SH
: CASHADDR_P2PKH));
} else {
textSize = btchip_public_key_to_encoded_base58(
address, 20 + versionSize, (unsigned char *)tmp,
sizeof(tmp), version, 1);
tmp[textSize] = '\0';
}
} else if (G_coin_config->native_segwit_prefix) {
textSize = segwit_addr_encode(
tmp, PIC(G_coin_config->native_segwit_prefix), 0,

Loading…
Cancel
Save