From e4f74b56f3a5efcc9e264fe02e6854fd71c32d82 Mon Sep 17 00:00:00 2001 From: Kristaps Kaupe Date: Wed, 2 Aug 2023 12:12:22 +0300 Subject: [PATCH] Deduplicate AES code --- jmbase/jmbase/__init__.py | 2 +- jmbase/jmbase/crypto.py | 15 +++++++++++++++ jmbase/setup.py | 2 +- jmbitcoin/jmbitcoin/secp256k1_ecies.py | 25 ++++++------------------- jmbitcoin/setup.py | 2 +- jmclient/jmclient/storage.py | 19 ++++++------------- jmclient/setup.py | 2 +- 7 files changed, 31 insertions(+), 36 deletions(-) create mode 100644 jmbase/jmbase/crypto.py diff --git a/jmbase/jmbase/__init__.py b/jmbase/jmbase/__init__.py index 55c27b338..7f16d8eff 100644 --- a/jmbase/jmbase/__init__.py +++ b/jmbase/jmbase/__init__.py @@ -15,4 +15,4 @@ JMHTTPResource, set_custom_stop_reactor) from .bytesprod import BytesProducer from .commands import * - +from .crypto import aes_cbc_encrypt, aes_cbc_decrypt diff --git a/jmbase/jmbase/crypto.py b/jmbase/jmbase/crypto.py new file mode 100644 index 000000000..e1e8fba2e --- /dev/null +++ b/jmbase/jmbase/crypto.py @@ -0,0 +1,15 @@ +import pyaes + +def aes_cbc_encrypt(key: bytes, data: bytes, iv: bytes) -> bytes: + encrypter = pyaes.Encrypter( + pyaes.AESModeOfOperationCBC(key, iv=iv)) + enc_data = encrypter.feed(data) + enc_data += encrypter.feed() + return enc_data + +def aes_cbc_decrypt(key: bytes, data: bytes, iv: bytes) -> bytes: + decrypter = pyaes.Decrypter( + pyaes.AESModeOfOperationCBC(key, iv=iv)) + dec_data = decrypter.feed(data) + dec_data += decrypter.feed() + return dec_data diff --git a/jmbase/setup.py b/jmbase/setup.py index 7ac0e3300..3b0e773d8 100644 --- a/jmbase/setup.py +++ b/jmbase/setup.py @@ -10,6 +10,6 @@ license='GPL', packages=['jmbase'], install_requires=['twisted==22.4.0', 'service-identity==21.1.0', - 'chromalog==1.0.5'], + 'chromalog==1.0.5', 'pyaes==1.6.1'], python_requires='>=3.6', zip_safe=False) diff --git a/jmbitcoin/jmbitcoin/secp256k1_ecies.py b/jmbitcoin/jmbitcoin/secp256k1_ecies.py index d719f33bc..0ae63dca5 100644 --- a/jmbitcoin/jmbitcoin/secp256k1_ecies.py +++ b/jmbitcoin/jmbitcoin/secp256k1_ecies.py @@ -1,9 +1,9 @@ import base64 import hmac import hashlib -import pyaes import os import jmbitcoin as btc +from jmbase import aes_cbc_encrypt, aes_cbc_decrypt from bitcointx.core.key import CPubKey ECIES_MAGIC_BYTES = b'BIE1' @@ -11,25 +11,12 @@ class ECIESDecryptionError(Exception): pass -# AES primitives. See BIP-SNICKER for specification. -def aes_encrypt(key, data, iv): - encrypter = pyaes.Encrypter( - pyaes.AESModeOfOperationCBC(key, iv=iv)) - enc_data = encrypter.feed(data) - enc_data += encrypter.feed() - - return enc_data - -def aes_decrypt(key, data, iv): - decrypter = pyaes.Decrypter( - pyaes.AESModeOfOperationCBC(key, iv=iv)) +def _ecies_aes_decrypt(key, data, iv): try: - dec_data = decrypter.feed(data) - dec_data += decrypter.feed() + return aes_cbc_decrypt(key, data, iv) except ValueError: # note decryption errors can come from PKCS7 padding errors raise ECIESDecryptionError() - return dec_data def ecies_encrypt(message, pubkey): """ Take a message in bytes and a secp256k1 public key @@ -53,10 +40,10 @@ def ecies_encrypt(message, pubkey): ecdh_key = btc.multiply(r, pubkey) key = hashlib.sha512(ecdh_key).digest() iv, key_e, key_m = key[0:16], key[16:32], key[32:] - ciphertext = aes_encrypt(key_e, message, iv=iv) + ciphertext = aes_cbc_encrypt(key_e, message, iv=iv) encrypted = ECIES_MAGIC_BYTES + R + ciphertext mac = hmac.new(key_m, encrypted, hashlib.sha256).digest() - return base64.b64encode(encrypted + mac) + return base64.b64encode(encrypted + mac) def ecies_decrypt(privkey, encrypted): if len(privkey) == 33 and privkey[-1] == 1: @@ -78,5 +65,5 @@ def ecies_decrypt(privkey, encrypted): iv, key_e, key_m = key[0:16], key[16:32], key[32:] if mac != hmac.new(key_m, encrypted[:-32], hashlib.sha256).digest(): raise ECIESDecryptionError() - return aes_decrypt(key_e, ciphertext, iv=iv) + return _ecies_aes_decrypt(key_e, ciphertext, iv=iv) diff --git a/jmbitcoin/setup.py b/jmbitcoin/setup.py index 8fb416373..4292046c5 100644 --- a/jmbitcoin/setup.py +++ b/jmbitcoin/setup.py @@ -10,5 +10,5 @@ license='GPL', packages=['jmbitcoin'], python_requires='>=3.6', - install_requires=['python-bitcointx==1.1.3', 'pyaes==1.6.1'], + install_requires=['python-bitcointx==1.1.3'], zip_safe=False) diff --git a/jmclient/jmclient/storage.py b/jmclient/jmclient/storage.py index 21f5aff66..33780e899 100644 --- a/jmclient/jmclient/storage.py +++ b/jmclient/jmclient/storage.py @@ -2,9 +2,9 @@ import shutil import atexit import bencoder -import pyaes from hashlib import sha256 from argon2 import low_level +from jmbase import aes_cbc_encrypt, aes_cbc_decrypt from .support import get_random_bytes @@ -252,20 +252,13 @@ def _decrypt_file(self, password, data): return self._decrypt(container[b'data'], container[b'enc'][b'iv']) - def _encrypt(self, data, iv): - encrypter = pyaes.Encrypter( - pyaes.AESModeOfOperationCBC(self._hash.hash, iv=iv)) - enc_data = encrypter.feed(self.MAGIC_DETECT_ENC + data) - enc_data += encrypter.feed() + def _encrypt(self, data: bytes, iv: bytes) -> bytes: + return aes_cbc_encrypt(self._hash.hash, + self.MAGIC_DETECT_ENC + data, iv) - return enc_data - - def _decrypt(self, data, iv): - decrypter = pyaes.Decrypter( - pyaes.AESModeOfOperationCBC(self._hash.hash, iv=iv)) + def _decrypt(self, data: bytes, iv: bytes) -> bytes: try: - dec_data = decrypter.feed(data) - dec_data += decrypter.feed() + dec_data = aes_cbc_decrypt(self._hash.hash, data, iv) except ValueError: # in most "wrong password" cases the pkcs7 padding will be wrong raise StoragePasswordError("Wrong password.") diff --git a/jmclient/setup.py b/jmclient/setup.py index f24e1eeba..1458bbf5c 100644 --- a/jmclient/setup.py +++ b/jmclient/setup.py @@ -11,7 +11,7 @@ packages=['jmclient'], install_requires=['joinmarketbase==0.9.10dev', 'mnemonic==0.20', 'argon2_cffi==21.3.0', 'bencoder.pyx==3.0.1', - 'pyaes==1.6.1', 'klein==20.6.0', 'pyjwt==2.4.0', + 'klein==20.6.0', 'pyjwt==2.4.0', 'autobahn==20.12.3', 'werkzeug==2.2.3'], python_requires='>=3.6', zip_safe=False)