Skip to content

Replace pycrypto with cryptography #251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,7 @@ tests/failed_blocks/
/test.py

.DS_Store
.pytest_cache
.venv
Pipfile
Pipfile.lock
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
'future',
'langdetect',
'prettytable',
'pycrypto>=1.9.1',
'cryptography',
'pylibscrypt>=1.6.1',
'scrypt>=0.8.0',
'toolz',
Expand Down
29 changes: 19 additions & 10 deletions steem/aes.py
Original file line number Diff line number Diff line change
@@ -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):
"""
Expand Down Expand Up @@ -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')
38 changes: 26 additions & 12 deletions steembase/bip38.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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):
Expand All @@ -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)
Expand Down Expand Up @@ -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)))
Expand Down
25 changes: 16 additions & 9 deletions steembase/memo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -84,22 +89,23 @@ 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 "
BS = 16
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),
Expand Down Expand Up @@ -130,7 +136,7 @@ def decode_memo(priv, message):
raw = raw[16:]
check = struct.unpack_from("<I", unhexlify(raw[:8]))[0]
raw = raw[8:]
cipher = raw
cipher_text = raw

if repr(to_key) == repr(priv.pubkey):
shared_secret = get_shared_secret(priv, from_key)
Expand All @@ -140,15 +146,16 @@ def decode_memo(priv, message):
raise ValueError("Incorrect PrivateKey")

" Init encryption "
aes, checksum = init_aes(shared_secret, nonce)
cipher, checksum = init_aes(shared_secret, nonce)

" Check "
assert check == checksum, "Checksum failure"

" Encryption "
# remove the varint prefix (FIXME, long messages!)
message = cipher[2:]
message = aes.decrypt(unhexlify(compat_bytes(message, 'ascii')))
message = cipher_text[2:]
decryptor = cipher.decryptor()
message = decryptor.update(unhexlify(compat_bytes(message, 'ascii'))) + decryptor.finalize()
try:
return _unpad(message.decode('utf8'), 16)
except: # noqa FIXME(sneak)
Expand Down