Enable Segwit Bech32 addresses

Increase the size of the addresses in protobuf.
Fix layout2.c to handle longer addresses.
Add a field bech32_prefix to coins.h
Adapted the coins-gen script.
Added bech32 support in signing.c and transaction.c
pull/25/head
Jochen Hoenicke 7 years ago committed by Pavol Rusnak
parent cf3dc6051c
commit 97581928de
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D

@ -53,6 +53,7 @@ OBJS += ../vendor/trezor-crypto/bip39.o
OBJS += ../vendor/trezor-crypto/pbkdf2.o
OBJS += ../vendor/trezor-crypto/base32.o
OBJS += ../vendor/trezor-crypto/base58.o
OBJS += ../vendor/trezor-crypto/segwit_addr.o
OBJS += ../vendor/trezor-crypto/ripemd160.o
OBJS += ../vendor/trezor-crypto/sha2.o

@ -23,9 +23,10 @@ def get_fields(coin):
'true' if coin['forkid'] is not None else 'false',
'%d' % coin['address_type'] if coin['address_type'] is not None else '0',
'%d' % coin['address_type_p2sh'] if coin['address_type_p2sh'] is not None else '0',
'0x%s' % coin['xpub_magic'] if coin['xpub_magic'] is not None else '00000000',
'0x%s' % coin['xprv_magic'] if coin['xprv_magic'] is not None else '00000000',
'%d' % coin['forkid'] if coin['forkid'] else '0'
'0x%s' % coin['xpub_magic'] if coin['xpub_magic'] is not None else '0x00000000',
'0x%s' % coin['xprv_magic'] if coin['xprv_magic'] is not None else '0x00000000',
'%d' % coin['forkid'] if coin['forkid'] else '0',
'"%s"' % coin['bech32_prefix'] if coin.get('bech32_prefix') is not None else 'NULL'
]

@ -40,6 +40,7 @@ typedef struct _CoinInfo {
uint32_t xpub_magic;
uint32_t xprv_magic;
uint32_t forkid;
const char *bech32_prefix;
} CoinInfo;
extern const CoinInfo coins[COINS_COUNT];

@ -694,7 +694,7 @@ void fsm_msgGetAddress(GetAddress *msg)
}
bool qrcode = false;
for (;;) {
layoutAddress(address, desc, qrcode);
layoutAddress(address, desc, qrcode, msg->script_type == InputScriptType_SPENDWITNESS);
if (protectButton(ButtonRequestType_ButtonRequest_Address, false)) {
break;
}
@ -732,7 +732,7 @@ void fsm_msgEthereumGetAddress(EthereumGetAddress *msg)
bool qrcode = false;
for (;;) {
layoutAddress(address, desc, qrcode);
layoutAddress(address, desc, qrcode, false);
if (protectButton(ButtonRequestType_ButtonRequest_Address, false)) {
break;
}
@ -1160,7 +1160,7 @@ void fsm_msgNEMGetAddress(NEMGetAddress *msg)
bool qrcode = false;
for (;;) {
layoutAddress(resp->address, desc, qrcode);
layoutAddress(resp->address, desc, qrcode, false);
if (protectButton(ButtonRequestType_ButtonRequest_Address, false)) {
break;
}

@ -277,7 +277,7 @@ void layoutResetWord(const char *word, int pass, int word_pos, bool last)
oledRefresh();
}
void layoutAddress(const char *address, const char *desc, bool qrcode)
void layoutAddress(const char *address, const char *desc, bool qrcode, bool ignorecase)
{
if (layoutLast != layoutAddress) {
layoutSwipe();
@ -286,40 +286,44 @@ void layoutAddress(const char *address, const char *desc, bool qrcode)
}
layoutLast = layoutAddress;
uint32_t addrlen = strlen(address);
if (qrcode) {
static unsigned char bitdata[QR_MAX_BITDATA];
int side = qr_encode(QR_LEVEL_M, 0, address, 0, bitdata);
char address_upcase[addrlen + 1];
if (ignorecase) {
for (uint32_t i = 0; i < addrlen + 1; i++) {
address_upcase[i] = address[i] >= 'a' && address[i] <= 'z' ?
address[i] + 'A' - 'a' : address[i];
}
}
int side = qr_encode(addrlen <= (ignorecase ? 60 : 40) ? QR_LEVEL_M : QR_LEVEL_L, 0,
ignorecase ? address_upcase : address, 0, bitdata);
oledInvert(0, 0, 63, 63);
if (side > 0 && side <= 29) {
oledInvert(0, 0, (side + 2) * 2, (side + 2) * 2);
int offset = 32 - side;
for (int i = 0; i < side; i++) {
for (int j = 0; j< side; j++) {
int a = j * side + i;
if (bitdata[a / 8] & (1 << (7 - a % 8))) {
oledClearPixel(2 + i * 2, 2 + j * 2);
oledClearPixel(3 + i * 2, 2 + j * 2);
oledClearPixel(2 + i * 2, 3 + j * 2);
oledClearPixel(3 + i * 2, 3 + j * 2);
oledBox(offset + i * 2, offset + j * 2,
offset + 1 + i * 2, offset + 1 + j * 2, false);
}
}
}
} else if (side > 0 && side <= 60) {
oledInvert(0, 0, (side + 3), (side + 3));
int offset = 32 - (side / 2);
for (int i = 0; i < side; i++) {
for (int j = 0; j< side; j++) {
int a = j * side + i;
if (bitdata[a / 8] & (1 << (7 - a % 8))) {
oledClearPixel(2 + i, 2 + j);
oledClearPixel(offset + i, offset + j);
}
}
}
}
} else {
uint32_t addrlen = strlen(address);
uint32_t rowlen = addrlen / 2;
if (addrlen % 2) {
rowlen++;
}
uint32_t rowlen = (addrlen - 1) / (addrlen <= 40 ? 2 : addrlen <= 60 ? 3 : 4) + 1;
const char **str = split_message((const uint8_t *)address, addrlen, rowlen);
if (desc) {
oledDrawString(0, 0 * 9, desc);

@ -50,7 +50,7 @@ void layoutCipherKeyValue(bool encrypt, const char *key);
void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing);
void layoutDecryptMessage(const uint8_t *msg, uint32_t len, const char *address);
void layoutResetWord(const char *word, int pass, int word_pos, bool last);
void layoutAddress(const char *address, const char *desc, bool qrcode);
void layoutAddress(const char *address, const char *desc, bool qrcode, bool ignorecase);
void layoutPublicKey(const uint8_t *pubkey);
void layoutSignIdentity(const IdentityType *identity, const char *challenge);
void layoutDecryptIdentity(const IdentityType *identity);

@ -33,7 +33,7 @@ PublicKey.xpub max_size:113
GetAddress.address_n max_count:8
GetAddress.coin_name max_size:21
Address.address max_size:60
Address.address max_size:76
EthereumGetAddress.address_n max_count:8
EthereumAddress.address max_size:20
@ -57,12 +57,12 @@ SignMessage.address_n max_count:8
SignMessage.message max_size:1024
SignMessage.coin_name max_size:21
VerifyMessage.address max_size:60
VerifyMessage.address max_size:76
VerifyMessage.signature max_size:65
VerifyMessage.message max_size:1024
VerifyMessage.coin_name max_size:21
MessageSignature.address max_size:60
MessageSignature.address max_size:76
MessageSignature.signature max_size:65
EthereumSignMessage.address_n max_count:8
@ -97,7 +97,7 @@ DecryptMessage skip_message:true
# deprecated
DecryptedMessage skip_message:true
# DecryptedMessage.address max_size:60
# DecryptedMessage.address max_size:76
# DecryptedMessage.message max_size:1024
CipherKeyValue.address_n max_count:8
@ -133,7 +133,7 @@ SignIdentity.challenge_hidden max_size:256
SignIdentity.challenge_visual max_size:256
SignIdentity.ecdsa_curve_name max_size:32
SignedIdentity.address max_size:60
SignedIdentity.address max_size:76
SignedIdentity.public_key max_size:33
SignedIdentity.signature max_size:65

@ -12,7 +12,7 @@ TxInputType.address_n max_count:8
TxInputType.prev_hash max_size:32
TxInputType.script_sig max_size:1650
TxOutputType.address max_size:54
TxOutputType.address max_size:76
TxOutputType.address_n max_count:8
TxOutputType.op_return_data max_size:80

@ -392,7 +392,7 @@ bool compile_input_script_sig(TxInputType *tinput)
if (!multisig_fp_mismatch) {
// check that this is still multisig
uint8_t h[32];
if (tinput->script_type != InputScriptType_SPENDMULTISIG
if (!tinput->has_multisig
|| cryptoMultisigFingerprint(&(tinput->multisig), h) == 0
|| memcmp(multisig_fp, h, 32) != 0) {
// Transaction has changed during signing
@ -469,8 +469,7 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinInf
static bool signing_check_input(TxInputType *txinput) {
/* compute multisig fingerprint */
/* (if all input share the same fingerprint, outputs having the same fingerprint will be considered as change outputs) */
if (txinput->has_multisig && !multisig_fp_mismatch
&& txinput->script_type == InputScriptType_SPENDMULTISIG) {
if (txinput->has_multisig && !multisig_fp_mismatch) {
uint8_t h[32];
if (cryptoMultisigFingerprint(&txinput->multisig, h) == 0) {
fsm_sendFailure(FailureType_Failure_ProcessError, _("Error computing multisig fingerprint"));
@ -705,12 +704,6 @@ static bool signing_sign_segwit_input(TxInputType *txinput) {
if (txinput->script_type == InputScriptType_SPENDWITNESS
|| txinput->script_type == InputScriptType_SPENDP2SHWITNESS) {
// disable native segwit for now
if (txinput->script_type == InputScriptType_SPENDWITNESS) {
fsm_sendFailure(FailureType_Failure_DataError, _("Native segwit is disabled"));
signing_abort();
return false;
}
if (!compile_input_script_sig(txinput)) {
fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile input"));
signing_abort();
@ -832,12 +825,6 @@ void signing_txack(TransactionType *tx)
signing_abort();
return;
}
// disable native segwit for now
if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS) {
fsm_sendFailure(FailureType_Failure_DataError, _("Native segwit is disabled"));
signing_abort();
return;
}
if (!tx->inputs[0].has_amount) {
fsm_sendFailure(FailureType_Failure_DataError, _("Segwit input without amount"));
signing_abort();

@ -31,6 +31,9 @@
#include "address.h"
#include "messages.pb.h"
#include "types.pb.h"
#include "segwit_addr.h"
#define SEGWIT_VERSION_0 0
static const uint8_t segwit_header[2] = {0,1};
@ -77,11 +80,12 @@ bool compute_address(const CoinInfo *coin,
}
if (script_type == InputScriptType_SPENDWITNESS) {
// segwit p2wsh: script hash is single sha256
if (!coin->has_segwit) {
if (!coin->has_segwit || !coin->bech32_prefix) {
return 0;
}
if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_0, digest, 32)) {
return 0;
}
// disable native segwit for now
return 0;
} else if (script_type == InputScriptType_SPENDP2SHWITNESS) {
// segwit p2wsh encapsuled in p2sh address
if (!coin->has_segwit) {
@ -111,11 +115,13 @@ bool compute_address(const CoinInfo *coin,
}
} else if (script_type == InputScriptType_SPENDWITNESS) {
// segwit p2wpkh: pubkey hash is ripemd160 of sha256
if (!coin->has_segwit) {
if (!coin->has_segwit || !coin->bech32_prefix) {
return 0;
}
ecdsa_get_pubkeyhash(node->public_key, digest);
if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_0, digest, 20)) {
return 0;
}
// disable native segwit for now
return 0;
} else if (script_type == InputScriptType_SPENDP2SHWITNESS) {
// segwit p2wpkh embedded in p2sh
if (!coin->has_segwit) {
@ -163,8 +169,8 @@ int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, T
input_script_type = InputScriptType_SPENDMULTISIG;
break;
case OutputScriptType_PAYTOWITNESS:
// disable native segwit for now
return 0;
input_script_type = InputScriptType_SPENDWITNESS;
break;
case OutputScriptType_PAYTOP2SHWITNESS:
input_script_type = InputScriptType_SPENDP2SHWITNESS;
break;
@ -187,8 +193,9 @@ int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, T
addr_raw_len = base58_decode_check(in->address, addr_raw, MAX_ADDR_RAW_SIZE);
size_t prefix_len;
if (address_check_prefix(addr_raw, coin->address_type) // p2pkh
&& addr_raw_len == 20 + (prefix_len = address_prefix_bytes_len(coin->address_type))) {
if (coin->has_address_type // p2pkh
&& address_check_prefix(addr_raw, coin->address_type)
&& addr_raw_len == 20 + (prefix_len = address_prefix_bytes_len(coin->address_type))) {
out->script_pubkey.bytes[0] = 0x76; // OP_DUP
out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160
out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes
@ -196,13 +203,26 @@ int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, T
out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY
out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG
out->script_pubkey.size = 25;
} else if (address_check_prefix(addr_raw, coin->address_type_p2sh) // p2sh
} else if (coin->has_address_type_p2sh // p2sh
&& address_check_prefix(addr_raw, coin->address_type_p2sh)
&& addr_raw_len == 20 + (prefix_len = address_prefix_bytes_len(coin->address_type_p2sh))) {
out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160
out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes
memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len, 20);
out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL
out->script_pubkey.size = 23;
} else if (coin->bech32_prefix) {
int witver;
if (!segwit_addr_decode(&witver, addr_raw, &addr_raw_len, coin->bech32_prefix, in->address)) {
return 0;
}
// segwit:
// push 1 byte version id (opcode OP_0 = 0, OP_i = 80+i)
// push addr_raw (segwit_addr_decode makes sure addr_raw_len is at most 40)
out->script_pubkey.bytes[0] = witver == 0 ? 0 : 80 + witver;
out->script_pubkey.bytes[1] = addr_raw_len;
memcpy(out->script_pubkey.bytes + 2, addr_raw, addr_raw_len);
out->script_pubkey.size = addr_raw_len + 2;
} else {
return 0;
}

Loading…
Cancel
Save