Skip to content

Commit

Permalink
bitcoin: disallow importing/sweeping segwit scripts with uncompressed…
Browse files Browse the repository at this point in the history
… pubkey

fixes spesmilo#4638
  • Loading branch information
SomberNight committed Apr 18, 2019
1 parent a1d98d4 commit d4a2e96
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 9 deletions.
7 changes: 7 additions & 0 deletions electrum/bitcoin.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,9 @@ def DecodeBase58Check(psz: Union[bytes, str]) -> bytes:
WIF_SCRIPT_TYPES_INV = inv_dict(WIF_SCRIPT_TYPES)


def is_segwit_script_type(txin_type: str) -> bool:
return txin_type in ('p2wpkh', 'p2wpkh-p2sh', 'p2wsh', 'p2wsh-p2sh')


def serialize_privkey(secret: bytes, compressed: bool, txin_type: str,
internal_use: bool=False) -> str:
Expand Down Expand Up @@ -576,6 +579,10 @@ def deserialize_privkey(key: str) -> Tuple[str, bytes, bool]:
if len(vch) not in [33, 34]:
raise BitcoinException('invalid vch len for WIF key: {}'.format(len(vch)))
compressed = len(vch) == 34

if is_segwit_script_type(txin_type) and not compressed:
raise BitcoinException('only compressed public keys can be used in segwit scripts')

secret_bytes = vch[1:33]
# we accept secrets outside curve range; cast into range here:
secret_bytes = ecc.ECPrivkey.normalize_secret_bytes(secret_bytes)
Expand Down
4 changes: 2 additions & 2 deletions electrum/plugins/ledger/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import traceback

from electrum import ecc
from electrum.bitcoin import TYPE_ADDRESS, int_to_hex, var_int
from electrum.bitcoin import TYPE_ADDRESS, int_to_hex, var_int, is_segwit_script_type
from electrum.bip32 import BIP32Node
from electrum.i18n import _
from electrum.keystore import Hardware_KeyStore
Expand Down Expand Up @@ -518,7 +518,7 @@ def show_address(self, sequence, txin_type):
client = self.get_client()
address_path = self.get_derivation()[2:] + "/%d/%d"%sequence
self.handler.show_message(_("Showing address ..."))
segwit = Transaction.is_segwit_inputtype(txin_type)
segwit = is_segwit_script_type(txin_type)
segwitNative = txin_type == 'p2wpkh'
try:
client.getWalletPublicKey(address_path, showOnScreen=True, segwit=segwit, segwitNative=segwitNative)
Expand Down
8 changes: 7 additions & 1 deletion electrum/tests/test_bitcoin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
is_b58_address, address_to_scripthash, is_minikey,
is_compressed_privkey, EncodeBase58Check, DecodeBase58Check,
script_num_to_hex, push_script, add_number_to_script, int_to_hex,
opcodes, base_encode, base_decode)
opcodes, base_encode, base_decode, BitcoinException)
from electrum.bip32 import (BIP32Node, convert_bip32_intpath_to_strpath,
xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
is_xpub, convert_bip32_path_to_list_of_uint32,
Expand Down Expand Up @@ -736,6 +736,12 @@ def test_is_compressed_privkey(self):
self.assertEqual(priv_details['compressed'],
is_compressed_privkey(priv_details['priv']))

@needs_test_with_all_ecc_implementations
def test_segwit_uncompressed_pubkey(self):
with self.assertRaises(BitcoinException):
is_private_key("p2wpkh-p2sh:5JKXxT3wAZHcybJ9YNkuHur9vou6uuAnorBV9A8vVxGNFH5wvTW",
raise_on_error=True)


class TestBaseEncode(SequentialTestCase):

Expand Down
8 changes: 2 additions & 6 deletions electrum/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
hash160_to_p2sh, hash160_to_p2pkh, hash_to_segwit_addr,
hash_encode, var_int, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC, COIN,
push_script, int_to_hex, push_script, b58_address_to_hash160,
opcodes, add_number_to_script, base_decode)
opcodes, add_number_to_script, base_decode, is_segwit_script_type)
from .crypto import sha256d
from .keystore import xpubkey_to_address, xpubkey_to_pubkey

Expand Down Expand Up @@ -815,11 +815,7 @@ def is_segwit_input(cls, txin, guess_for_address=False):
if _type == 'address' and guess_for_address:
_type = cls.guess_txintype_from_address(txin['address'])
has_nonzero_witness = txin.get('witness', '00') not in ('00', None)
return cls.is_segwit_inputtype(_type) or has_nonzero_witness

@classmethod
def is_segwit_inputtype(cls, txin_type):
return txin_type in ('p2wpkh', 'p2wpkh-p2sh', 'p2wsh', 'p2wsh-p2sh')
return is_segwit_script_type(_type) or has_nonzero_witness

@classmethod
def guess_txintype_from_address(cls, addr):
Expand Down

0 comments on commit d4a2e96

Please sign in to comment.