Multicoin support - support specific dashboard icon, fix coin ID in message signature, implement GET COIN VERSION / SET ALTERNATE COIN VERSION

Multicoin support
This commit is contained in:
BTChip 2016-10-17 23:11:50 +02:00
parent 170945c40e
commit 7266eed882
16 changed files with 189 additions and 33 deletions

3
.gitignore vendored
View File

@ -2,6 +2,7 @@ bin
debug
dep
obj
src/u2f_crypto_data.h
src/glyphs.c

View File

@ -1,7 +1,7 @@
# BTC Application TODOs
- [X] Sign message support
- [ ] Altcoins fixes (signing message format, prompts)
- [X] Altcoins fixes (signing message format, prompts)
- [ ] Support an arbitrary number of TX outputs
- [ ] Support device / PIN locking in next firmware update

BIN
glyphs/icon_litecoin.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icon_litecoin.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

View File

@ -58,6 +58,7 @@
#define BTCHIP_INS_SET_USER_KEYCARD 0x10
#define BTCHIP_INS_SETUP_SECURE_SCREEN 0x12
#define BTCHIP_INS_SET_ALTERNATE_COIN_VER 0x14
#define BTCHIP_INS_GET_COIN_VER 0x16
#define BTCHIP_INS_STORE_TRUST_ROOT_BIP70 0x30
#define BTCHIP_INS_CREATE_CERTIFICATE_BIP70 0x32
@ -152,5 +153,6 @@ unsigned short btchip_apdu_get_random(void);
unsigned short btchip_apdu_get_firmware_version(void);
unsigned short btchip_apdu_set_alternate_coin_version(void);
unsigned short btchip_apdu_get_coin_version(void);
#endif

View File

@ -24,6 +24,8 @@
#include "btchip_filesystem_tx.h"
#define MAX_OUTPUT_TO_CHECK 500
#define MAX_COIN_ID 10
#define MAX_SHORT_COIN_ID 4
#define MAGIC_TRUSTED_INPUT 0x32
#define MAGIC_DEV_KEY 0x01
@ -143,6 +145,14 @@ struct btchip_context_s {
unsigned char payToAddressVersion;
/** Current Pay To Script Hash version */
unsigned char payToScriptHashVersion;
/** Current Coin ID */
unsigned char coinId[MAX_COIN_ID];
/** Current short Coin ID */
unsigned char shortCoinId[MAX_SHORT_COIN_ID];
/** Current Coin ID length */
unsigned char coinIdLength;
/** Current short Coin ID length */
unsigned char shortCoinIdLength;
/** Non protected transaction context */
@ -192,6 +202,7 @@ struct btchip_context_s {
// was previously in NVRAM
btchip_transaction_summary_t transactionSummary;
unsigned short hashedMessageLength;
union {

View File

@ -37,6 +37,14 @@ struct btchip_config_s {
unsigned char options;
unsigned char payToAddressVersion;
unsigned char payToScriptHashVersion;
/** Current Coin ID */
unsigned char coinId[MAX_COIN_ID];
/** Current short Coin ID */
unsigned char shortCoinId[MAX_SHORT_COIN_ID];
/** Current Coin ID length */
unsigned char coinIdLength;
/** Current short Coin ID length */
unsigned char shortCoinIdLength;
};
typedef struct btchip_config_s btchip_config_t;

View File

@ -0,0 +1,47 @@
/*******************************************************************************
* Ledger Blue - Bitcoin Wallet
* (c) 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.
********************************************************************************/
#include "btchip_internal.h"
#include "btchip_apdu_constants.h"
#define P1_VERSION_ONLY 0x00
#define P1_VERSION_COINID 0x01
unsigned short btchip_apdu_get_coin_version() {
uint8_t offset = 0;
SB_CHECK(N_btchip.bkp.config.operationMode);
if ((SB_GET(N_btchip.bkp.config.operationMode) ==
BTCHIP_MODE_SETUP_NEEDED) ||
(SB_GET(N_btchip.bkp.config.operationMode) == BTCHIP_MODE_ISSUER)) {
return BTCHIP_SW_CONDITIONS_OF_USE_NOT_SATISFIED;
}
G_io_apdu_buffer[offset++] = btchip_context_D.payToAddressVersion;
G_io_apdu_buffer[offset++] = btchip_context_D.payToScriptHashVersion;
G_io_apdu_buffer[offset++] = btchip_context_D.coinIdLength;
os_memmove(G_io_apdu_buffer + offset, btchip_context_D.coinId,
btchip_context_D.coinIdLength);
offset += btchip_context_D.coinIdLength;
G_io_apdu_buffer[offset++] = btchip_context_D.shortCoinIdLength;
os_memmove(G_io_apdu_buffer + offset, btchip_context_D.shortCoinId,
btchip_context_D.shortCoinIdLength);
offset += btchip_context_D.shortCoinIdLength;
btchip_context_D.outLength = offset;
return BTCHIP_SW_OK;
}

View File

@ -18,9 +18,25 @@
#include "btchip_internal.h"
#include "btchip_apdu_constants.h"
#define P1_VERSION_ONLY 0x00
#define P1_VERSION_COINID 0x01
unsigned short btchip_apdu_set_alternate_coin_version() {
if (G_io_apdu_buffer[ISO_OFFSET_LC] != 0x02) {
return BTCHIP_SW_INCORRECT_LENGTH;
uint8_t offset = ISO_OFFSET_CDATA;
unsigned char p1 = G_io_apdu_buffer[ISO_OFFSET_P1];
if ((p1 != P1_VERSION_ONLY) && (p1 != P1_VERSION_COINID)) {
return BTCHIP_SW_INCORRECT_P1_P2;
}
if (p1 == P1_VERSION_ONLY) {
if (G_io_apdu_buffer[ISO_OFFSET_LC] != 0x02) {
return BTCHIP_SW_INCORRECT_LENGTH;
}
} else {
if (G_io_apdu_buffer[ISO_OFFSET_LC] >
4 + MAX_COIN_ID + MAX_SHORT_COIN_ID) {
return BTCHIP_SW_INCORRECT_LENGTH;
}
}
SB_CHECK(N_btchip.bkp.config.operationMode);
@ -34,9 +50,23 @@ unsigned short btchip_apdu_set_alternate_coin_version() {
return BTCHIP_SW_SECURITY_STATUS_NOT_SATISFIED;
}
btchip_context_D.payToAddressVersion = G_io_apdu_buffer[ISO_OFFSET_CDATA];
btchip_context_D.payToScriptHashVersion =
G_io_apdu_buffer[ISO_OFFSET_CDATA + 1];
btchip_context_D.payToAddressVersion = G_io_apdu_buffer[offset++];
btchip_context_D.payToScriptHashVersion = G_io_apdu_buffer[offset++];
if (p1 == P1_VERSION_COINID) {
uint8_t coinIdLength = G_io_apdu_buffer[offset];
uint8_t shortCoinIdLength = G_io_apdu_buffer[offset + 1 + coinIdLength];
if ((coinIdLength > MAX_COIN_ID) ||
(shortCoinIdLength > MAX_SHORT_COIN_ID)) {
return BTCHIP_SW_INCORRECT_DATA;
}
os_memmove(btchip_context_D.coinId, G_io_apdu_buffer + offset + 1,
coinIdLength);
btchip_context_D.coinIdLength = coinIdLength;
offset += 1 + coinIdLength;
os_memmove(btchip_context_D.shortCoinId, G_io_apdu_buffer + offset + 1,
shortCoinIdLength);
btchip_context_D.shortCoinIdLength = shortCoinIdLength;
}
return BTCHIP_SW_OK;
}

View File

@ -34,13 +34,13 @@ void btchip_autosetup() {
// supporting multi output
SB_SET(config.supportedModes, BTCHIP_MODE_WALLET);
SB_SET(config.operationMode, BTCHIP_MODE_WALLET);
#ifdef HAVE_DEFAULT_TESTNET
config.payToAddressVersion = 111;
config.payToScriptHashVersion = 196;
#else
config.payToAddressVersion = 0;
config.payToScriptHashVersion = 5;
#endif
config.payToAddressVersion = BTCHIP_P2PKH_VERSION;
config.payToScriptHashVersion = BTCHIP_P2SH_VERSION;
config.coinIdLength = strlen(BTCHIP_COINID);
os_memmove(config.coinId, BTCHIP_COINID, config.coinIdLength);
config.shortCoinIdLength = strlen(BTCHIP_COINID_SHORT);
os_memmove(config.shortCoinId, BTCHIP_COINID_SHORT,
config.shortCoinIdLength);
nvm_write((void *)&N_btchip.bkp.config, &config, sizeof(config));
cx_rng(tmp, sizeof(tmp));
cx_des_init_key(tmp, sizeof(tmp), &desKey);

View File

@ -121,6 +121,13 @@ unsigned short btchip_apdu_sign_message_internal() {
cx_sha256_init(&btchip_context_D.transactionHashFull);
cx_sha256_init(
&btchip_context_D.transactionHashAuthorization);
chunkLength =
btchip_context_D.coinIdLength + SIGNMAGIC_LENGTH;
cx_hash(&btchip_context_D.transactionHashFull.header, 0,
&chunkLength, 1, NULL);
cx_hash(&btchip_context_D.transactionHashFull.header, 0,
btchip_context_D.coinId,
btchip_context_D.coinIdLength, NULL);
cx_hash(&btchip_context_D.transactionHashFull.header, 0,
(unsigned char *)SIGNMAGIC, SIGNMAGIC_LENGTH, NULL);
if (btchip_context_D.transactionSummary.messageLength <

View File

@ -44,6 +44,14 @@ void btchip_context_init() {
N_btchip.bkp.config.payToAddressVersion;
btchip_context_D.payToScriptHashVersion =
N_btchip.bkp.config.payToScriptHashVersion;
btchip_context_D.coinIdLength = N_btchip.bkp.config.coinIdLength;
os_memmove(btchip_context_D.coinId, N_btchip.bkp.config.coinId,
N_btchip.bkp.config.coinIdLength);
btchip_context_D.shortCoinIdLength =
N_btchip.bkp.config.shortCoinIdLength;
os_memmove(btchip_context_D.shortCoinId,
N_btchip.bkp.config.shortCoinId,
N_btchip.bkp.config.shortCoinIdLength);
SB_CHECK(N_btchip.bkp.config.operationMode);
}
if (!N_btchip.storageInitialized) {

View File

@ -40,9 +40,8 @@ unsigned char const BASE58ALPHABET[] = {
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
unsigned char const SIGNMAGIC[] = {0x18, 'B', 'i', 't', 'c', 'o', 'i', 'n', ' ',
'S', 'i', 'g', 'n', 'e', 'd', ' ', 'M', 'e',
's', 's', 'a', 'g', 'e', ':', '\n'};
unsigned char const SIGNMAGIC[] = {' ', 'S', 'i', 'g', 'n', 'e', 'd', ' ', 'M',
'e', 's', 's', 'a', 'g', 'e', ':', '\n'};
unsigned char const TWOPOWER[] = {0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80};
@ -61,6 +60,7 @@ unsigned char const DISPATCHER_CLA[] = {
BTCHIP_CLA, // btchip_apdu_get_random,
BTCHIP_CLA, // btchip_apdu_get_firmware_version,
BTCHIP_CLA, // btchip_apdu_set_alternate_coin_version
BTCHIP_CLA, // btchip_apdu_get_coin_version
};
unsigned char const DISPATCHER_INS[] = {
@ -77,6 +77,7 @@ unsigned char const DISPATCHER_INS[] = {
BTCHIP_INS_GET_RANDOM, // btchip_apdu_get_random,
BTCHIP_INS_GET_FIRMWARE_VERSION, // btchip_apdu_get_firmware_version,
BTCHIP_INS_SET_ALTERNATE_COIN_VER, // btchip_apdu_set_alternate_coin_version
BTCHIP_INS_GET_COIN_VER, // btchip_apdu_get_coin_version
};
unsigned char const DISPATCHER_DATA_IN[] = {
@ -93,6 +94,7 @@ unsigned char const DISPATCHER_DATA_IN[] = {
0, // btchip_apdu_get_random,
0, // btchip_apdu_get_firmware_version,
1, // btchip_apdu_set_alternate_coin_version
0, // btchip_apdu_get_coin_version
};
apduProcessingFunction const DISPATCHER_FUNCTIONS[] = {
@ -109,4 +111,5 @@ apduProcessingFunction const DISPATCHER_FUNCTIONS[] = {
btchip_apdu_get_random,
btchip_apdu_get_firmware_version,
btchip_apdu_set_alternate_coin_version,
btchip_apdu_get_coin_version,
};

View File

@ -17,7 +17,7 @@
#include "btchip_internal.h"
#define SIGNMAGIC_LENGTH 25
#define SIGNMAGIC_LENGTH 17
extern unsigned char const WIDE HEXDIGITS[16];
extern unsigned char const WIDE BASE58TABLE[128];
@ -49,7 +49,7 @@ extern unsigned char const WIDE TWOPOWER[8];
#define APDU_BASE_LENGTH 13
#define DISPATCHER_APDUS 13
#define DISPATCHER_APDUS 14
typedef unsigned short (*WIDE apduProcessingFunction)(void);

View File

@ -144,7 +144,8 @@ const bagl_element_t ui_idle_nanos[] = {
NULL,
NULL},
{{BAGL_ICON, 0x01, 17, 9, 14, 14, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
#ifdef COIN_BITCOIN
{{BAGL_ICON, 0x01, 12, 9, 14, 14, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_BITCOIN_BADGE},
NULL,
0,
@ -153,8 +154,9 @@ const bagl_element_t ui_idle_nanos[] = {
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 38, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px, 0},
#endif // COIN_BITCOIN
{{BAGL_LABELINE, 0x01, 33, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"Use wallet to",
0,
0,
@ -162,8 +164,8 @@ const bagl_element_t ui_idle_nanos[] = {
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x01, 39, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px, 0},
{{BAGL_LABELINE, 0x01, 34, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px, 0},
"view accounts",
0,
0,
@ -986,10 +988,38 @@ unsigned char io_event(unsigned char channel) {
case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT:
if (UX_DISPLAYED()) {
// TODO perform actions after all screen elements have been
// displayed
UX_DISPLAYED_:
#ifdef COIN_LITECOIN
// extra elements per screen, only for idle screen
if (ux.elements == ui_idle_nanos && ui_idle_nanos_state == 0) {
// could be used to perform extra print after an array has been
// displayed
switch (ux.elements_count - ux.elements_current) {
case 0: {
extern unsigned int const C_icon_litecoin_colors[];
extern unsigned char const C_icon_litecoin_bitmap[];
io_seproxyhal_display_bitmap(12, 9, 14, 14,
C_icon_litecoin_colors, 1,
C_icon_litecoin_bitmap);
break;
}
// case 1: // next extra element
// case 2: // next next extra element ...
default:
break;
}
ux.elements_current++;
}
#endif // COIN_LITECOIN
;
} else {
UX_DISPLAY_PROCESSED_EVENT();
// nothing displayed, then it's likely the end of the screen
if (!io_seproxyhal_spi_is_status_sent()) {
goto UX_DISPLAYED_;
}
}
break;
@ -1101,18 +1131,27 @@ uint8_t prepare_full_output(unsigned int outputPos) {
4);
// Prepare amount
// TODO : match current coin version
strcpy(fullAmount, "BTC ");
btchip_context_D.tmp = (unsigned char *)fullAmount + 4;
os_memmove(fullAmount, btchip_context_D.shortCoinId,
btchip_context_D.shortCoinIdLength);
fullAmount[btchip_context_D.shortCoinIdLength] = ' ';
btchip_context_D.tmp =
(unsigned char *)(fullAmount +
btchip_context_D.shortCoinIdLength + 1);
textSize = btchip_convert_hex_amount_to_displayable(amount);
fullAmount[textSize + 4] = '\0';
fullAmount[textSize + btchip_context_D.shortCoinIdLength + 1] =
'\0';
// prepare fee display
strcpy(feesAmount, "BTC ");
btchip_context_D.tmp = (unsigned char *)feesAmount + 4;
os_memmove(feesAmount, btchip_context_D.shortCoinId,
btchip_context_D.shortCoinIdLength);
feesAmount[btchip_context_D.shortCoinIdLength] = ' ';
btchip_context_D.tmp =
(unsigned char *)(feesAmount +
btchip_context_D.shortCoinIdLength + 1);
textSize = btchip_convert_hex_amount_to_displayable(fees);
feesAmount[textSize + 4] = '\0';
feesAmount[textSize + btchip_context_D.shortCoinIdLength + 1] =
'\0';
break;
}
} else {