diff --git a/contrib/requirements/requirements.txt b/contrib/requirements/requirements.txt index 1fd6f984a..529b3882f 100644 --- a/contrib/requirements/requirements.txt +++ b/contrib/requirements/requirements.txt @@ -10,4 +10,3 @@ aiohttp>=3.3.0 aiohttp_socks certifi bitstring -cryptography diff --git a/electrum/lnonion.py b/electrum/lnonion.py index 66d8975f5..5a3f3d18b 100644 --- a/electrum/lnonion.py +++ b/electrum/lnonion.py @@ -23,12 +23,17 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import sys import hashlib from typing import Sequence, List, Tuple, NamedTuple, TYPE_CHECKING from enum import IntEnum, IntFlag -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms -from cryptography.hazmat.backends import default_backend +try: + from Cryptodome.Cipher import ChaCha20 +except Exception as e: + print(e) + print("Error: pycryptodomex >= 3.7 not available.") + sys.exit(1) from . import ecc from .crypto import sha256, hmac_oneshot @@ -227,10 +232,8 @@ def generate_filler(key_type: bytes, num_hops: int, hop_size: int, def generate_cipher_stream(stream_key: bytes, num_bytes: int) -> bytes: - algo = algorithms.ChaCha20(stream_key, nonce=bytes(16)) - cipher = Cipher(algo, mode=None, backend=default_backend()) - encryptor = cipher.encryptor() - return encryptor.update(bytes(num_bytes)) + cipher = ChaCha20.new(key=stream_key, nonce=bytes(8)) + return cipher.encrypt(bytes(num_bytes)) ProcessedOnionPacket = NamedTuple("ProcessedOnionPacket", [("are_we_final", bool), diff --git a/electrum/lntransport.py b/electrum/lntransport.py index fe51e911c..a51e208fd 100644 --- a/electrum/lntransport.py +++ b/electrum/lntransport.py @@ -5,10 +5,16 @@ # Derived from https://gist.github.com/AdamISZ/046d05c156aaeb56cc897f85eecb3eb8 +import sys import hashlib from asyncio import StreamReader, StreamWriter -import cryptography.hazmat.primitives.ciphers.aead as AEAD +try: + from Cryptodome.Cipher import ChaCha20_Poly1305 +except Exception as e: + print(e) + print("Error: pycryptodomex >= 3.7 not available.") + sys.exit(1) from .crypto import sha256, hmac_oneshot from .lnutil import (get_ecdh, privkey_to_pubkey, LightningPeerConnectionClosed, @@ -38,16 +44,19 @@ def get_nonce_bytes(n): """ return b"\x00"*4 + n.to_bytes(8, 'little') -def aead_encrypt(k, nonce, associated_data, data): +def aead_encrypt(key: bytes, nonce: int, associated_data: bytes, data: bytes) -> bytes: nonce_bytes = get_nonce_bytes(nonce) - a = AEAD.ChaCha20Poly1305(k) - return a.encrypt(nonce_bytes, data, associated_data) + cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce_bytes) + cipher.update(associated_data) + ciphertext, mac = cipher.encrypt_and_digest(plaintext=data) + return ciphertext + mac -def aead_decrypt(k, nonce, associated_data, data): +def aead_decrypt(key: bytes, nonce: int, associated_data: bytes, data: bytes) -> bytes: nonce_bytes = get_nonce_bytes(nonce) - a = AEAD.ChaCha20Poly1305(k) - #raises InvalidTag exception if it's not valid - return a.decrypt(nonce_bytes, data, associated_data) + cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce_bytes) + cipher.update(associated_data) + # raises ValueError if not valid (e.g. incorrect MAC) + return cipher.decrypt_and_verify(ciphertext=data[:-16], received_mac_tag=data[-16:]) def get_bolt8_hkdf(salt, ikm): """RFC5869 HKDF instantiated in the specific form