commit fa389a4e06efa2bd369ad1d9c879d54efe2816af
parent c59ac49feadeb6905aef17990b0f93414fd93574
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:
2 files changed, 29 insertions(+), 34 deletions(-)
diff --git a/electrum/crypto.py b/electrum/crypto.py
@@ -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):
@@ -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)
@@ -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')
@@ -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")
diff --git a/electrum/tests/test_bitcoin.py b/electrum/tests/test_bitcoin.py
@@ -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
@@ -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)
@@ -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)
@@ -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)
@@ -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)