keystore.check_password: raise better exc if called on pwless ks
If keystore.check_password is called with some pw on a keystore that does not have a password set,
it now raises better exceptions: it should now always raise InvalidPassword, and with a nicer msg.
Previously the exc type would depend on the ks type.
Examples before change:
```
>>> wallet.keystore.check_password("asd")
Traceback (most recent call last):
File "/home/user/wspace/electrum/electrum/keystore.py", line 580, in check_password
xprv = pw_decode(self.xprv, password, version=self.pw_hash_version)
File "/home/user/wspace/electrum/electrum/crypto.py", line 311, in pw_decode
plaintext_bytes = pw_decode_bytes(data, password, version=version)
File "/home/user/wspace/electrum/electrum/crypto.py", line 270, in pw_decode_bytes
data_bytes = bytes(base64.b64decode(data))
File "/usr/lib/python3.10/base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
```
```
>>> wallet.keystore.check_password("asd")
Traceback (most recent call last):
s = aes_decrypt_with_iv(secret, iv, e)
File "/home/user/wspace/electrum/electrum/crypto.py", line 157, in aes_decrypt_with_iv
data = decryptor.update(data) + decryptor.finalize()
File "/usr/lib/python3/dist-packages/cryptography/hazmat/primitives/ciphers/base.py", line 148, in finalize
data = self._ctx.finalize()
File "/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/ciphers.py", line 193, in finalize
raise ValueError(
ValueError: The length of the provided data is not a multiple of the block length.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/user/wspace/electrum/electrum/gui/qt/console.py", line 254, in exec_command
result = eval(command, self.namespace, self.namespace)
File "<string>", line 1, in <module>
File "/home/user/wspace/electrum/electrum/keystore.py", line 248, in check_password
self.get_private_key(pubkey, password)
File "/home/user/wspace/electrum/electrum/keystore.py", line 267, in get_private_key
sec = pw_decode(self.keypairs[pubkey], password, version=self.pw_hash_version)
File "/home/user/wspace/electrum/electrum/crypto.py", line 311, in pw_decode
plaintext_bytes = pw_decode_bytes(data, password, version=version)
File "/home/user/wspace/electrum/electrum/crypto.py", line 271, in pw_decode_bytes
return _pw_decode_raw(data_bytes, password, version=version)
File "/home/user/wspace/electrum/electrum/crypto.py", line 255, in _pw_decode_raw
raise InvalidPassword() from e
electrum.util.InvalidPassword: Incorrect password
```
-----
Examples after change:
```
>>> wallet.keystore.check_password("asd")
Traceback (most recent call last):
return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "...\electrum\keystore.py", line 68, in wrapper
return check_password_fn(self, password)
File "...\electrum\keystore.py", line 605, in check_password
xprv = pw_decode(self.xprv, password, version=self.pw_hash_version)
File "...\electrum\crypto.py", line 311, in pw_decode
plaintext_bytes = pw_decode_bytes(data, password, version=version)
File "...\electrum\crypto.py", line 267, in pw_decode_bytes
raise CiphertextFormatError("ciphertext not valid base64") from e
electrum.crypto.CiphertextFormatError: ciphertext not valid base64
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "...\electrum\gui\qt\console.py", line 254, in exec_command
result = eval(command, self.namespace, self.namespace)
File "<string>", line 1, in <module>
File "...\electrum\keystore.py", line 76, in wrapper
raise InvalidPassword("password given but keystore has no password") from e
electrum.util.InvalidPassword: password given but keystore has no password
```
```
>>> wallet.keystore.check_password("asd")
Traceback (most recent call last):
s = aes_decrypt_with_iv(secret, iv, e)
File "...\electrum\crypto.py", line 158, in aes_decrypt_with_iv
data = cipher.decrypt(data)
File "...\Python310\site-packages\Cryptodome\Cipher\_mode_cbc.py", line 246, in decrypt
raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "...\electrum\keystore.py", line 68, in wrapper
return check_password_fn(self, password)
File "...\electrum\keystore.py", line 272, in check_password
self.get_private_key(pubkey, password)
File "...\electrum\keystore.py", line 291, in get_private_key
sec = pw_decode(self.keypairs[pubkey], password, version=self.pw_hash_version)
File "...\electrum\crypto.py", line 311, in pw_decode
plaintext_bytes = pw_decode_bytes(data, password, version=version)
File "...\electrum\crypto.py", line 268, in pw_decode_bytes
return _pw_decode_raw(data_bytes, password, version=version)
File "...\electrum\crypto.py", line 249, in _pw_decode_raw
raise InvalidPassword() from e
electrum.util.InvalidPassword: Incorrect password
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "...\electrum\gui\qt\console.py", line 254, in exec_command
result = eval(command, self.namespace, self.namespace)
File "<string>", line 1, in <module>
File "...\electrum\keystore.py", line 76, in wrapper
raise InvalidPassword("password given but keystore has no password") from e
electrum.util.InvalidPassword: password given but keystore has no password
```
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
# SOFTWARE.
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import os
|
||||
import sys
|
||||
import hashlib
|
||||
@@ -109,6 +110,10 @@ class InvalidPadding(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CiphertextFormatError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def append_PKCS7_padding(data: bytes) -> bytes:
|
||||
assert_bytes(data)
|
||||
padlen = 16 - (len(data) % 16)
|
||||
@@ -256,7 +261,10 @@ def pw_decode_bytes(data: str, password: Union[bytes, str], *, version:int) -> b
|
||||
"""base64 ciphertext -> plaintext bytes"""
|
||||
if version not in KNOWN_PW_HASH_VERSIONS:
|
||||
raise UnexpectedPasswordHashVersion(version)
|
||||
data_bytes = bytes(base64.b64decode(data))
|
||||
try:
|
||||
data_bytes = bytes(base64.b64decode(data, validate=True))
|
||||
except binascii.Error as e:
|
||||
raise CiphertextFormatError("ciphertext not valid base64") from e
|
||||
return _pw_decode_raw(data_bytes, password, version=version)
|
||||
|
||||
|
||||
@@ -273,7 +281,10 @@ def pw_encode_with_version_and_mac(data: bytes, password: Union[bytes, str]) ->
|
||||
|
||||
def pw_decode_with_version_and_mac(data: str, password: Union[bytes, str]) -> bytes:
|
||||
"""base64 ciphertext -> plaintext bytes"""
|
||||
data_bytes = bytes(base64.b64decode(data))
|
||||
try:
|
||||
data_bytes = bytes(base64.b64decode(data, validate=True))
|
||||
except binascii.Error as e:
|
||||
raise CiphertextFormatError("ciphertext not valid base64") from e
|
||||
version = int(data_bytes[0])
|
||||
encrypted = data_bytes[1:-4]
|
||||
mac = data_bytes[-4:]
|
||||
|
||||
Reference in New Issue
Block a user