Change address support for segwit.

Rewrote change address support for segwit.
Also checks the bip32 path of change address.
pull/25/head
Jochen Hoenicke 7 years ago
parent 97a061244e
commit 7b1381766f
No known key found for this signature in database
GPG Key ID: 65B10C0466560648

@ -62,6 +62,9 @@ static uint32_t next_nonsegwit_input;
static uint32_t progress, progress_step, progress_meta_step;
static bool multisig_fp_set, multisig_fp_mismatch;
static uint8_t multisig_fp[32];
static uint32_t in_address_n[8];
static size_t in_address_n_count;
/* progress_step/meta_step are fixed point numbers, giving the
* progress per input in permille with these many additional bits.
@ -298,6 +301,40 @@ void phase2_request_next_input(void)
}
}
void set_input_bip32_path(const TxInputType *tinput)
{
size_t count = tinput->address_n_count;
if (count < 2) {
// no change address allowed
in_address_n_count = (size_t) -1;
} else if (in_address_n_count == 0) {
// initialize in_address_n on first input seen
in_address_n_count = count;
memcpy(in_address_n, tinput->address_n, (count - 2) * sizeof(uint32_t));
} else if (in_address_n_count != count
|| memcmp(in_address_n, tinput->address_n, (count-2) * sizeof(uint32_t)) != 0) {
// mismatch -> no change address allowed
in_address_n_count = (size_t) -1;
}
}
bool check_change_bip32_path(const TxOutputType *toutput)
{
size_t count = toutput->address_n_count;
// check that the last two components specify a sane address on the change chain
if (count < 2
|| toutput->address_n[count-2] != 1
|| toutput->address_n[count-1] > 1000000)
return 0;
// check that the other components exactly match input.
if (in_address_n_count != count
|| memcmp(in_address_n, toutput->address_n, (count-2) * sizeof(uint32_t)) != 0)
return 0;
return 1;
}
bool compile_input_script_sig(TxInputType *tinput)
{
if (!multisig_fp_mismatch) {
@ -349,6 +386,7 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinTyp
// this means 50 % per phase.
progress_step = (500 << PROGRESS_PRECISION) / inputs_count;
in_address_n_count = 0;
multisig_fp_set = false;
multisig_fp_mismatch = false;
next_nonsegwit_input = 0xffffffff;
@ -406,6 +444,7 @@ void signing_txack(TransactionType *tx)
} else { // single signature
multisig_fp_mismatch = true;
}
set_input_bip32_path(&tx->inputs[0]);
// compute segwit hashPrevouts & hashSequence
tx_prevout_hash(&hashers[0], &tx->inputs[0]);
tx_sequence_hash(&hashers[1], &tx->inputs[0]);
@ -520,19 +559,28 @@ void signing_txack(TransactionType *tx)
* Ask for permission.
*/
bool is_change = false;
if (tx->outputs[0].script_type == OutputScriptType_PAYTOMULTISIG) {
uint8_t h[32];
if (!multisig_fp_set || multisig_fp_mismatch
|| cryptoMultisigFingerprint(&(tx->outputs[0].multisig), h) == 0
|| memcmp(multisig_fp, h, 32) != 0) {
fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig change address");
if (tx->outputs[0].address_n_count > 0) {
if (tx->outputs[0].has_address) {
fsm_sendFailure(FailureType_Failure_Other, "Address in change output");
signing_abort();
return;
}
is_change = true;
} else if (tx->outputs[0].script_type == OutputScriptType_PAYTOADDRESS &&
tx->outputs[0].address_n_count > 0) {
is_change = true;
if (tx->outputs[0].script_type == OutputScriptType_PAYTOMULTISIG) {
uint8_t h[32];
if (!multisig_fp_set || multisig_fp_mismatch
|| cryptoMultisigFingerprint(&(tx->outputs[0].multisig), h) == 0
|| memcmp(multisig_fp, h, 32) != 0) {
fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig change address");
signing_abort();
return;
}
is_change = check_change_bip32_path(&tx->outputs[0]);
} else if (tx->outputs[0].script_type == OutputScriptType_PAYTOADDRESS
|| ((tx->outputs[0].script_type == OutputScriptType_PAYTOWITNESS
|| tx->outputs[0].script_type == OutputScriptType_PAYTOP2SHWITNESS)
&& tx->outputs[0].amount < segwit_to_spend)) {
is_change = check_change_bip32_path(&tx->outputs[0]);
}
}
if (is_change) {
@ -545,7 +593,7 @@ void signing_txack(TransactionType *tx)
}
}
if (spending + tx->inputs[0].amount < spending) {
if (spending + tx->outputs[0].amount < spending) {
fsm_sendFailure(FailureType_Failure_Other, "Value overflow");
signing_abort();
}

@ -58,132 +58,201 @@ uint32_t op_push(uint32_t i, uint8_t *out) {
return 5;
}
bool compute_address(const CoinType *coin,
InputScriptType script_type,
const HDNode *node,
bool has_multisig, const MultisigRedeemScriptType *multisig,
char address[MAX_ADDR_SIZE],
bool *is_segwit) {
uint8_t raw[32];
uint8_t digest[MAX_ADDR_RAW_SIZE];
size_t prelen;
if (has_multisig) {
if (cryptoMultisigPubkeyIndex(multisig, node->public_key) < 0) {
return 0;
}
if (compile_script_multisig_hash(multisig, digest) == 0) {
return 0;
}
if (script_type == InputScriptType_SPENDWITNESS) {
// segwit p2wsh: script hash is single sha256
*is_segwit = 1;
if (!coin->has_address_type_p2wsh) {
return 0;
}
prelen = address_prefix_bytes_len(coin->address_type_p2wsh);
address_write_prefix_bytes(coin->address_type_p2wsh, raw);
raw[prelen] = 0; // version byte
raw[prelen + 1] = 0; // always 0, see bip-142
memcpy(raw+prelen+2, digest, 32);
if (!base58_encode_check(raw, prelen + 34, address, MAX_ADDR_SIZE)) {
return 0;
}
} else if (script_type == InputScriptType_SPENDP2SHWITNESS) {
// segwit p2wsh encapsuled in p2sh address
*is_segwit = 1;
if (!coin->has_address_type_p2sh) {
return 0;
}
raw[0] = 0; // push version
raw[1] = 32; // push 32 bytes
memcpy(raw+2, digest, 32); // push hash
sha256_Raw(raw, 34, digest);
prelen = address_prefix_bytes_len(coin->address_type_p2sh);
address_write_prefix_bytes(coin->address_type_p2sh, raw);
ripemd160(digest, 32, raw + prelen);
if (!base58_encode_check(raw, prelen + 20, address, MAX_ADDR_SIZE)) {
return 0;
}
} else {
// non-segwit p2sh multisig
*is_segwit = 0;
prelen = address_prefix_bytes_len(coin->address_type_p2sh);
address_write_prefix_bytes(coin->address_type_p2sh, raw);
ripemd160(digest, 32, raw + prelen);
if (!base58_encode_check(raw, prelen + 20, address, MAX_ADDR_SIZE)) {
return 0;
}
}
} else if (script_type == InputScriptType_SPENDWITNESS) {
// segwit p2wpkh: pubkey hash is ripemd160 of sha256
*is_segwit = 1;
if (!coin->has_address_type_p2wpkh) {
return 0;
}
prelen = address_prefix_bytes_len(coin->address_type_p2wpkh);
address_write_prefix_bytes(coin->address_type_p2wpkh, raw);
raw[prelen] = 0; // version byte
raw[prelen + 1] = 0; // always 0, see bip-142
ecdsa_get_pubkeyhash(node->public_key, raw + prelen + 2);
if (!base58_encode_check(raw, prelen + 22, address, MAX_ADDR_SIZE)) {
return 0;
}
} else if (script_type == InputScriptType_SPENDP2SHWITNESS) {
// segwit p2wpkh embedded in p2sh
*is_segwit = 1;
if (!coin->has_address_type_p2sh) {
return 0;
}
prelen = address_prefix_bytes_len(coin->address_type_p2sh);
raw[0] = 0; // version byte
raw[1] = 20; // push 20 bytes
ecdsa_get_pubkeyhash(node->public_key, raw + 2);
sha256_Raw(raw, 22, digest);
address_write_prefix_bytes(coin->address_type_p2sh, raw);
ripemd160(digest, 32, raw + prelen);
if (!base58_encode_check(raw, prelen + 20, address, MAX_ADDR_SIZE)) {
return 0;
}
} else {
*is_segwit = 0;
ecdsa_get_address(node->public_key, coin->address_type, address, MAX_ADDR_SIZE);
}
return 1;
}
int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm)
{
memset(out, 0, sizeof(TxOutputBinType));
out->amount = in->amount;
uint8_t addr_raw[MAX_ADDR_RAW_SIZE];
size_t addr_raw_len;
if (in->has_address) { // address provided -> regular output
addr_raw_len = base58_decode_check(in->address, addr_raw, MAX_ADDR_RAW_SIZE);
if (in->script_type != OutputScriptType_PAYTOADDRESS) {
// allow for p2sh (backward compatibility only)
if (in->script_type != OutputScriptType_PAYTOSCRIPTHASH
|| addr_raw_len != 21
|| addr_raw[0] != coin->address_type_p2sh) {
return 0;
}
bool is_segwit;
if (in->address_n_count > 0) {
HDNode node;
InputScriptType input_script_type;
switch (in->script_type) {
case OutputScriptType_PAYTOOPRETURN:
// only 0 satoshi allowed for OP_RETURN
if (in->amount != 0)
return 0; // failed to compile output
uint32_t r = 0;
out->script_pubkey.bytes[0] = 0x6A; r++; // OP_RETURN
r += op_push(in->op_return_data.size, out->script_pubkey.bytes + r);
memcpy(out->script_pubkey.bytes + r, in->op_return_data.bytes, in->op_return_data.size); r += in->op_return_data.size;
out->script_pubkey.size = r;
return r;
case OutputScriptType_PAYTOADDRESS:
input_script_type = InputScriptType_SPENDADDRESS;
break;
case OutputScriptType_PAYTOMULTISIG:
input_script_type = InputScriptType_SPENDMULTISIG;
break;
case OutputScriptType_PAYTOWITNESS:
input_script_type = InputScriptType_SPENDWITNESS;
break;
case OutputScriptType_PAYTOP2SHWITNESS:
input_script_type = InputScriptType_SPENDP2SHWITNESS;
break;
default:
return 0; // failed to compile output
}
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))) {
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
memcpy(out->script_pubkey.bytes + 3, addr_raw + prefix_len, 20);
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
&& 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 (address_check_prefix(addr_raw, coin->address_type_p2wpkh)
&& addr_raw_len == 22 + (prefix_len = address_prefix_bytes_len(coin->address_type_p2wpkh))
&& addr_raw[prefix_len] == 0 && addr_raw[prefix_len + 1] == 0) { // p2wpkh v0
out->script_pubkey.bytes[0] = 0x00; // version 0
out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes
memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len + 2, 20);
out->script_pubkey.size = 22;
} else if (address_check_prefix(addr_raw, coin->address_type_p2wsh)
&& addr_raw_len == 34 + (prefix_len = address_prefix_bytes_len(coin->address_type_p2wsh))
&& addr_raw[prefix_len] == 0 && addr_raw[prefix_len + 1] == 0) { // p2wsh v0
out->script_pubkey.bytes[0] = 0x00; // version 0
out->script_pubkey.bytes[1] = 0x20; // pushing 32 bytes
memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len + 2, 32);
out->script_pubkey.size = 34;
} else {
return 0;
memcpy(&node, root, sizeof(HDNode));
if (hdnode_private_ckd_cached(&node, in->address_n, in->address_n_count, NULL) == 0) {
return 0; // failed to compile output
}
if (needs_confirm) {
layoutConfirmOutput(coin, in);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return -1;
}
hdnode_fill_public_key(&node);
if (!compute_address(coin, input_script_type, &node,
in->has_multisig, &in->multisig,
in->address, &is_segwit)) {
return 0; // failed to compile output
}
return out->script_pubkey.size;
} else if (!in->has_address) {
return 0; // failed to compile output
}
if (in->script_type == OutputScriptType_PAYTOADDRESS) {
// address_n provided-> change address -> calculate from address_n
if (in->script_type == OutputScriptType_PAYTOADDRESS &&
in->address_n_count > 0) {
HDNode node;
memcpy(&node, root, sizeof(HDNode));
if (hdnode_private_ckd_cached(&node, in->address_n, in->address_n_count, NULL) == 0) {
return 0;
}
layoutProgressUpdate(true);
hdnode_get_address_raw(&node, coin->address_type, addr_raw);
} else { // does not have address_n neither address -> error
return 0;
}
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))) {
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
memcpy(out->script_pubkey.bytes + 3, addr_raw + address_prefix_bytes_len(coin->address_type), 20);
memcpy(out->script_pubkey.bytes + 3, addr_raw + prefix_len, 20);
out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY
out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG
out->script_pubkey.size = 25;
return 25;
}
if (in->script_type == OutputScriptType_PAYTOMULTISIG) {
uint8_t buf[32];
size_t prefix_bytes = address_prefix_bytes_len(coin->address_type_p2sh);
if (!in->has_multisig) {
return 0;
}
if (compile_script_multisig_hash(&(in->multisig), buf) == 0) {
return 0;
}
address_write_prefix_bytes(coin->address_type_p2sh, addr_raw);
ripemd160(buf, 32, addr_raw + prefix_bytes);
if (needs_confirm) {
base58_encode_check(addr_raw, prefix_bytes + 20, in->address, sizeof(in->address));
layoutConfirmOutput(coin, in);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return -1;
}
}
} else if (address_check_prefix(addr_raw, coin->address_type_p2sh) // 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_bytes, 20);
memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len, 20);
out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL
out->script_pubkey.size = 23;
return 23;
} else if (address_check_prefix(addr_raw, coin->address_type_p2wpkh)
&& addr_raw_len == 22 + (prefix_len = address_prefix_bytes_len(coin->address_type_p2wpkh))
&& addr_raw[prefix_len] == 0 && addr_raw[prefix_len + 1] == 0) { // p2wpkh v0
out->script_pubkey.bytes[0] = 0x00; // version 0
out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes
memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len + 2, 20);
out->script_pubkey.size = 22;
} else if (address_check_prefix(addr_raw, coin->address_type_p2wsh)
&& addr_raw_len == 34 + (prefix_len = address_prefix_bytes_len(coin->address_type_p2wsh))
&& addr_raw[prefix_len] == 0 && addr_raw[prefix_len + 1] == 0) { // p2wsh v0
out->script_pubkey.bytes[0] = 0x00; // version 0
out->script_pubkey.bytes[1] = 0x20; // pushing 32 bytes
memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len + 2, 32);
out->script_pubkey.size = 34;
} else {
return 0;
}
if (in->script_type == OutputScriptType_PAYTOOPRETURN) {
if (in->amount != 0) return 0; // only 0 satoshi allowed for OP_RETURN
uint32_t r = 0;
out->script_pubkey.bytes[0] = 0x6A; r++; // OP_RETURN
r += op_push(in->op_return_data.size, out->script_pubkey.bytes + r);
memcpy(out->script_pubkey.bytes + r, in->op_return_data.bytes, in->op_return_data.size); r += in->op_return_data.size;
out->script_pubkey.size = r;
return r;
if (needs_confirm) {
layoutConfirmOutput(coin, in);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return -1; // user aborted
}
}
return 0;
return out->script_pubkey.size;
}
uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out)

@ -45,6 +45,7 @@ typedef struct {
SHA256_CTX ctx;
} TxStruct;
bool compute_address(const CoinType *coin, InputScriptType script_type, const HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, char address[MAX_ADDR_SIZE], bool *is_segwit);
uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out);
uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t *out);
uint32_t compile_script_multisig_hash(const MultisigRedeemScriptType *multisig, uint8_t *hash);

Loading…
Cancel
Save