URI: 
       tMerge pull request #4937 from SomberNight/revert_password_v2 - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit fa389a4e06efa2bd369ad1d9c879d54efe2816af
   DIR parent c59ac49feadeb6905aef17990b0f93414fd93574
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Tue, 18 Dec 2018 19:35:57 +0100
       
       Merge pull request #4937 from SomberNight/revert_password_v2
       
       keystore: revert KDF change
       Diffstat:
         M electrum/crypto.py                  |      53 ++++++++++++++-----------------
         M electrum/tests/test_bitcoin.py      |      10 +++++-----
       
       2 files changed, 29 insertions(+), 34 deletions(-)
       ---
   DIR diff --git a/electrum/crypto.py b/electrum/crypto.py
       t@@ -116,9 +116,11 @@ def DecodeAES_bytes(secret: bytes, ciphertext: bytes) -> bytes:
            return s
        
        
       -PW_HASH_VERSION_LATEST = 2
       -KNOWN_PW_HASH_VERSIONS = (1, 2)
       +PW_HASH_VERSION_LATEST = 1
       +KNOWN_PW_HASH_VERSIONS = (1, 2, )
       +SUPPORTED_PW_HASH_VERSIONS = (1, )
        assert PW_HASH_VERSION_LATEST in KNOWN_PW_HASH_VERSIONS
       +assert PW_HASH_VERSION_LATEST in SUPPORTED_PW_HASH_VERSIONS
        
        
        class UnexpectedPasswordHashVersion(InvalidPassword):
       t@@ -126,23 +128,30 @@ class UnexpectedPasswordHashVersion(InvalidPassword):
                self.version = version
        
            def __str__(self):
       -        return "{unexpected}: {version}\n{please_update}".format(
       +        return "{unexpected}: {version}\n{instruction}".format(
                    unexpected=_("Unexpected password hash version"),
                    version=self.version,
       -            please_update=_('You are most likely using an outdated version of Electrum. Please update.'))
       +            instruction=_('You are most likely using an outdated version of Electrum. Please update.'))
        
        
       -def _hash_password(password: Union[bytes, str], *, version: int, salt: bytes) -> bytes:
       +class UnsupportedPasswordHashVersion(InvalidPassword):
       +    def __init__(self, version):
       +        self.version = version
       +
       +    def __str__(self):
       +        return "{unsupported}: {version}\n{instruction}".format(
       +            unsupported=_("Unsupported password hash version"),
       +            version=self.version,
       +            instruction=f"To open this wallet, try 'git checkout password_v{self.version}'.\n"
       +                        "Alternatively, restore from seed.")
       +
       +
       +def _hash_password(password: Union[bytes, str], *, version: int) -> bytes:
            pw = to_bytes(password, 'utf8')
       +    if version not in SUPPORTED_PW_HASH_VERSIONS:
       +        raise UnsupportedPasswordHashVersion(version)
            if version == 1:
                return sha256d(pw)
       -    elif version == 2:
       -        if not isinstance(salt, bytes) or len(salt) < 16:
       -            raise Exception('too weak salt', salt)
       -        return hashlib.pbkdf2_hmac(hash_name='sha256',
       -                                   password=pw,
       -                                   salt=b'ELECTRUM_PW_HASH_V2'+salt,
       -                                   iterations=50_000)
            else:
                assert version not in KNOWN_PW_HASH_VERSIONS
                raise UnexpectedPasswordHashVersion(version)
       t@@ -154,17 +163,9 @@ def pw_encode(data: str, password: Union[bytes, str, None], *, version: int) -> 
            if version not in KNOWN_PW_HASH_VERSIONS:
                raise UnexpectedPasswordHashVersion(version)
            # derive key from password
       -    if version == 1:
       -        salt = b''
       -    elif version == 2:
       -        salt = bytes(os.urandom(16))
       -    else:
       -        assert False, version
       -    secret = _hash_password(password, version=version, salt=salt)
       +    secret = _hash_password(password, version=version)
            # encrypt given data
       -    e = EncodeAES_bytes(secret, to_bytes(data, "utf8"))
       -    # return base64(salt + encrypted data)
       -    ciphertext = salt + e
       +    ciphertext = EncodeAES_bytes(secret, to_bytes(data, "utf8"))
            ciphertext_b64 = base64.b64encode(ciphertext)
            return ciphertext_b64.decode('utf8')
        
       t@@ -176,13 +177,7 @@ def pw_decode(data: str, password: Union[bytes, str, None], *, version: int) -> 
                raise UnexpectedPasswordHashVersion(version)
            data_bytes = bytes(base64.b64decode(data))
            # derive key from password
       -    if version == 1:
       -        salt = b''
       -    elif version == 2:
       -        salt, data_bytes = data_bytes[:16], data_bytes[16:]
       -    else:
       -        assert False, version
       -    secret = _hash_password(password, version=version, salt=salt)
       +    secret = _hash_password(password, version=version)
            # decrypt given data
            try:
                d = to_string(DecodeAES_bytes(secret, data_bytes), "utf8")
   DIR diff --git a/electrum/tests/test_bitcoin.py b/electrum/tests/test_bitcoin.py
       t@@ -11,7 +11,7 @@ from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key,
        from electrum.bip32 import (bip32_root, bip32_public_derivation, bip32_private_derivation,
                                    xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
                                    is_xpub, convert_bip32_path_to_list_of_uint32)
       -from electrum.crypto import sha256d, KNOWN_PW_HASH_VERSIONS
       +from electrum.crypto import sha256d, SUPPORTED_PW_HASH_VERSIONS
        from electrum import ecc, crypto, constants
        from electrum.ecc import number_to_string, string_to_number
        from electrum.transaction import opcodes
       t@@ -219,7 +219,7 @@ class Test_bitcoin(SequentialTestCase):
                """Make sure AES is homomorphic."""
                payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0'
                password = u'secret'
       -        for version in KNOWN_PW_HASH_VERSIONS:
       +        for version in SUPPORTED_PW_HASH_VERSIONS:
                    enc = crypto.pw_encode(payload, password, version=version)
                    dec = crypto.pw_decode(enc, password, version=version)
                    self.assertEqual(dec, payload)
       t@@ -228,7 +228,7 @@ class Test_bitcoin(SequentialTestCase):
            def test_aes_encode_without_password(self):
                """When not passed a password, pw_encode is noop on the payload."""
                payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0'
       -        for version in KNOWN_PW_HASH_VERSIONS:
       +        for version in SUPPORTED_PW_HASH_VERSIONS:
                    enc = crypto.pw_encode(payload, None, version=version)
                    self.assertEqual(payload, enc)
        
       t@@ -236,7 +236,7 @@ class Test_bitcoin(SequentialTestCase):
            def test_aes_deencode_without_password(self):
                """When not passed a password, pw_decode is noop on the payload."""
                payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0'
       -        for version in KNOWN_PW_HASH_VERSIONS:
       +        for version in SUPPORTED_PW_HASH_VERSIONS:
                    enc = crypto.pw_decode(payload, None, version=version)
                    self.assertEqual(payload, enc)
        
       t@@ -246,7 +246,7 @@ class Test_bitcoin(SequentialTestCase):
                payload = u"blah"
                password = u"uber secret"
                wrong_password = u"not the password"
       -        for version in KNOWN_PW_HASH_VERSIONS:
       +        for version in SUPPORTED_PW_HASH_VERSIONS:
                    enc = crypto.pw_encode(payload, password, version=version)
                    with self.assertRaises(InvalidPassword):
                        crypto.pw_decode(enc, wrong_password, version=version)