diff --git a/contrib/requirements/requirements.txt b/contrib/requirements/requirements.txt index a419b0bfd..312d955a1 100644 --- a/contrib/requirements/requirements.txt +++ b/contrib/requirements/requirements.txt @@ -1,4 +1,3 @@ -pyaes>=0.1a1 ecdsa>=0.14 qrcode protobuf>=3.12 diff --git a/electrum/crypto.py b/electrum/crypto.py index 7fda99aaa..3a0678535 100644 --- a/electrum/crypto.py +++ b/electrum/crypto.py @@ -30,12 +30,18 @@ import hashlib import hmac from typing import Union -import pyaes - from .util import assert_bytes, InvalidPassword, to_bytes, to_string, WalletFileException from .i18n import _ +HAS_PYAES = False +try: + import pyaes +except: + pass +else: + HAS_PYAES = True + HAS_CRYPTODOME = False try: from Cryptodome.Cipher import ChaCha20_Poly1305 as CD_ChaCha20_Poly1305 @@ -97,10 +103,12 @@ def aes_encrypt_with_iv(key: bytes, iv: bytes, data: bytes) -> bytes: cipher = CG_Cipher(CG_algorithms.AES(key), CG_modes.CBC(iv), backend=CG_default_backend()) encryptor = cipher.encryptor() e = encryptor.update(data) + encryptor.finalize() - else: + elif HAS_PYAES: aes_cbc = pyaes.AESModeOfOperationCBC(key, iv=iv) aes = pyaes.Encrypter(aes_cbc, padding=pyaes.PADDING_NONE) e = aes.feed(data) + aes.feed() # empty aes.feed() flushes buffer + else: + raise Exception("no AES backend found") return e @@ -113,10 +121,12 @@ def aes_decrypt_with_iv(key: bytes, iv: bytes, data: bytes) -> bytes: cipher = CG_Cipher(CG_algorithms.AES(key), CG_modes.CBC(iv), backend=CG_default_backend()) decryptor = cipher.decryptor() data = decryptor.update(data) + decryptor.finalize() - else: + elif HAS_PYAES: aes_cbc = pyaes.AESModeOfOperationCBC(key, iv=iv) aes = pyaes.Decrypter(aes_cbc, padding=pyaes.PADDING_NONE) data = aes.feed(data) + aes.feed() # empty aes.feed() flushes buffer + else: + raise Exception("no AES backend found") try: return strip_PKCS7_padding(data) except InvalidPadding: diff --git a/electrum/tests/test_bitcoin.py b/electrum/tests/test_bitcoin.py index a22fc6b16..9d5ed59ef 100644 --- a/electrum/tests/test_bitcoin.py +++ b/electrum/tests/test_bitcoin.py @@ -46,18 +46,21 @@ def needs_test_with_all_aes_implementations(func): return has_cryptodome = crypto.HAS_CRYPTODOME has_cryptography = crypto.HAS_CRYPTOGRAPHY + has_pyaes = crypto.HAS_PYAES try: - (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = False, False - func(*args, **kwargs) # pyaes + if has_pyaes: + (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, False, True + func(*args, **kwargs) # pyaes if has_cryptodome: - (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = True, False + (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = True, False, False func(*args, **kwargs) # cryptodome if has_cryptography: - (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = False, True + (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, True, False func(*args, **kwargs) # cryptography finally: crypto.HAS_CRYPTODOME = has_cryptodome crypto.HAS_CRYPTOGRAPHY = has_cryptography + crypto.HAS_PYAES = has_pyaes return run_test @@ -101,6 +104,10 @@ class Test_bitcoin(ElectrumTestCase): # we want the unit testing framework to test with cryptography available. self.assertTrue(bool(crypto.HAS_CRYPTOGRAPHY)) + def test_pyaes_is_available(self): + # we want the unit testing framework to test with pyaes available. + self.assertTrue(bool(crypto.HAS_PYAES)) + @needs_test_with_all_aes_implementations def test_crypto(self): for message in [b"Chancellor on brink of second bailout for banks", b'\xff'*512]: diff --git a/run_electrum b/run_electrum index 7102725fb..67776d94f 100755 --- a/run_electrum +++ b/run_electrum @@ -60,7 +60,6 @@ def check_imports(): # pure-python dependencies need to be imported here for pyinstaller try: import dns - import pyaes import ecdsa import certifi import qrcode diff --git a/setup.py b/setup.py index 53701e690..68fac9e69 100755 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ extras_require = { 'hardware': requirements_hw, 'gui': ['pyqt5'], 'crypto': ['cryptography>=2.1'], - 'tests': ['pycryptodomex>=3.7', 'cryptography>=2.1'], + 'tests': ['pycryptodomex>=3.7', 'cryptography>=2.1', 'pyaes>=0.1a1'], } # 'full' extra that tries to grab everything an enduser would need (except for libsecp256k1...) extras_require['full'] = [pkg for sublist in