diff --git a/.gitignore b/.gitignore index b990971..f84b3e5 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,7 @@ tests/failed_blocks/ /test.py .DS_Store +.pytest_cache +.venv +Pipfile +Pipfile.lock diff --git a/setup.py b/setup.py index 615cc6a..af73284 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ 'future', 'langdetect', 'prettytable', - 'pycrypto>=1.9.1', + 'cryptography', 'pylibscrypt>=1.6.1', 'scrypt>=0.8.0', 'toolz', diff --git a/steem/aes.py b/steem/aes.py index ca9f9d2..bceb9f9 100644 --- a/steem/aes.py +++ b/steem/aes.py @@ -1,8 +1,11 @@ -from Crypto import Random -from Crypto.Cipher import AES -import hashlib import base64 +import hashlib +import os +from cryptography.hazmat.primitives.ciphers import Cipher +from cryptography.hazmat.primitives.ciphers import algorithms +from cryptography.hazmat.primitives.ciphers import modes +from cryptography.hazmat.backends import default_backend class AESCipher(object): """ @@ -34,13 +37,19 @@ def _unpad(s): def encrypt(self, raw): raw = self._pad(AESCipher.str_to_bytes(raw)) - iv = Random.new().read(AES.block_size) - cipher = AES.new(self.key, AES.MODE_CBC, iv) - return base64.b64encode(iv + cipher.encrypt(raw)).decode('utf-8') + iv = os.urandom(self.bs) + backend = default_backend() + cipher = Cipher(algorithms.AES(self.key), modes.CBC(iv), backend=backend) + encryptor = cipher.encryptor() + cipher_text = encryptor.update(raw) + encryptor.finalize() + return base64.b64encode(iv + cipher_text).decode('utf-8') def decrypt(self, enc): enc = base64.b64decode(enc) - iv = enc[:AES.block_size] - cipher = AES.new(self.key, AES.MODE_CBC, iv) - return self._unpad(cipher.decrypt( - enc[AES.block_size:])).decode('utf-8') + iv = enc[:algorithms.AES.block_size] + backend = default_backend() + cipher = Cipher(algorithms.AES(self.key), modes.CBC(iv), + backend=backend) + decryptor = cipher.decryptor() + plain_text = decryptor.update(enc) + decryptor.finalize() + return self._unpad(plain_text ).decode('utf-8') diff --git a/steembase/bip38.py b/steembase/bip38.py index acbe4e3..07c0e2c 100644 --- a/steembase/bip38.py +++ b/steembase/bip38.py @@ -4,16 +4,21 @@ import sys from binascii import hexlify, unhexlify +from cryptography.hazmat.primitives.ciphers import Cipher +from cryptography.hazmat.primitives.ciphers import algorithms +from cryptography.hazmat.primitives.ciphers import modes +from cryptography.hazmat.backends import default_backend + from .account import PrivateKey from .base58 import Base58, base58decode from steem.utils import compat_bytes + log = logging.getLogger(__name__) -try: - from Crypto.Cipher import AES -except ImportError: - raise ImportError("Missing dependency: pycrypto") + + + SCRYPT_MODULE = os.environ.get('SCRYPT_MODULE', None) if not SCRYPT_MODULE: @@ -47,10 +52,11 @@ class SaltException(Exception): pass -def _encrypt_xor(a, b, aes): +def _encrypt_xor(a, b, cipher): """ Returns encrypt(a ^ b). """ a = unhexlify('%0.32x' % (int((a), 16) ^ int(hexlify(b), 16))) - return aes.encrypt(a) + encryptor = cipher.encryptor() + return encryptor.update(a) + encryptor.finalize() def encrypt(privkey, passphrase): @@ -77,9 +83,11 @@ def encrypt(privkey, passphrase): else: raise ValueError("No scrypt module loaded") (derived_half1, derived_half2) = (key[:32], key[32:]) - aes = AES.new(derived_half2) - encrypted_half1 = _encrypt_xor(privkeyhex[:32], derived_half1[:16], aes) - encrypted_half2 = _encrypt_xor(privkeyhex[32:], derived_half1[16:], aes) + backend = default_backend() + cipher = Cipher(algorithms.AES(derived_half2), modes.ECB(), backend=backend) + encrypted_half1 = _encrypt_xor(privkeyhex[:32], derived_half1[:16], cipher) + + encrypted_half2 = _encrypt_xor(privkeyhex[32:], derived_half1[16:], cipher) " flag byte is forced 0xc0 because Graphene only uses compressed keys " payload = ( b'\x01' + b'\x42' + b'\xc0' + salt + encrypted_half1 + encrypted_half2) @@ -121,9 +129,15 @@ def decrypt(encrypted_privkey, passphrase): derivedhalf2 = key[32:64] encryptedhalf1 = d[0:16] encryptedhalf2 = d[16:32] - aes = AES.new(derivedhalf2) - decryptedhalf2 = aes.decrypt(encryptedhalf2) - decryptedhalf1 = aes.decrypt(encryptedhalf1) + + backend = default_backend() + cipher = Cipher(algorithms.AES(derivedhalf2), modes.ECB(), backend=backend) + decryptor = cipher.decryptor() + decryptedhalf2 = decryptor.update(encryptedhalf2) + decryptor.finalize() + + decryptor = cipher.decryptor() + decryptedhalf1 = decryptor.update(encryptedhalf1) + decryptor.finalize() + privraw = decryptedhalf1 + decryptedhalf2 privraw = ('%064x' % (int(hexlify(privraw), 16) ^ int(hexlify(derivedhalf1), 16))) diff --git a/steembase/memo.py b/steembase/memo.py index ab6434e..5bfbd89 100644 --- a/steembase/memo.py +++ b/steembase/memo.py @@ -5,7 +5,10 @@ from binascii import hexlify, unhexlify from collections import OrderedDict -from Crypto.Cipher import AES +from cryptography.hazmat.primitives.ciphers import Cipher +from cryptography.hazmat.primitives.ciphers import algorithms +from cryptography.hazmat.primitives.ciphers.modes import CBC +from cryptography.hazmat.backends import default_backend from .operations import Memo from .base58 import base58encode, base58decode @@ -56,7 +59,9 @@ def init_aes(shared_secret, nonce): " AES " key = unhexlify(encryption_key[0:64]) iv = unhexlify(encryption_key[64:96]) - return AES.new(key, AES.MODE_CBC, iv), check + backend = default_backend() + cipher = Cipher(algorithms.AES(key), CBC(iv), backend=backend) + return cipher, check def _pad(s, BS): @@ -84,7 +89,7 @@ def encode_memo(priv, pub, nonce, message, **kwargs): """ from steembase import transactions shared_secret = get_shared_secret(priv, pub) - aes, check = init_aes(shared_secret, nonce) + cipher, check = init_aes(shared_secret, nonce) raw = compat_bytes(message, 'utf8') " Padding " @@ -92,14 +97,15 @@ def encode_memo(priv, pub, nonce, message, **kwargs): if len(raw) % BS: raw = _pad(raw, BS) " Encryption " - cipher = hexlify(aes.encrypt(raw)).decode('ascii') + encryptor = cipher.encryptor() + cipher_text = hexlify(encryptor.update(raw) + encryptor.finalize()).decode('ascii') prefix = kwargs.pop("prefix", default_prefix) s = OrderedDict([ ("from", format(priv.pubkey, prefix)), ("to", format(pub, prefix)), ("nonce", nonce), ("check", check), - ("encrypted", cipher), + ("encrypted", cipher_text), ("from_priv", repr(priv)), ("to_pub", repr(pub)), ("shared_secret", shared_secret), @@ -130,7 +136,7 @@ def decode_memo(priv, message): raw = raw[16:] check = struct.unpack_from("