mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-22 12:32:02 +00:00
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
This commit is contained in:
parent
cf3dc6051c
commit
97581928de
@ -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…
Reference in New Issue
Block a user