from cryptography.fernet import Fernet, base64, InvalidSignature, InvalidToken import hashlib from django.contrib.auth.hashers import make_password, PBKDF2PasswordHasher, BasePasswordHasher, get_random_string import logging log = logging.getLogger(__name__) import requests class JsonApi(object): @classmethod def get(cls, base_url, api_url): url = '{}{}'.format(base_url, api_url) data = {} response = None try: response = requests.get(url) response.raise_for_status() data = response.json() except Exception as exc: log.warning('GET failed: {} - {}'.format(url, exc)) if response is not None and hasattr(response, 'content'): log.warning('RESPONSE {}'.format(response.content)) finally: return data @classmethod def post(cls, base_url, api_url, data): url = '{}{}'.format(base_url, api_url) response_data = {} response = None try: response = requests.post(url, json=data) response.raise_for_status() if response.status_code == 201: log.info('Peer {} accepted block.'.format(base_url)) if not len(response.content): if response.status_code == 304: log.warning('Peer {}: unable to accept block.'.format(base_url)) else: response_data = response.json() except Exception as exc: log.warning('POST failed: {} - {}'.format(url, exc)) if response is not None and hasattr(response, 'content'): log.warning('RESPONSE {}'.format(response.content)) finally: return response_data class SymmetricEncryption(object): """ AES256 encryption driven through Fernet library """ @staticmethod def generate_key(): return Fernet.generate_key() @staticmethod def safe_encode(value): if type(value) is str: value = value.encode('utf-8') return base64.urlsafe_b64encode(value) @staticmethod def generate_salt(length=12): return get_random_string(length=length) @classmethod def build_encryption_key(cls, password_hash): reduced = password_hash[:32].encode('utf-8') return base64.urlsafe_b64encode(reduced) @staticmethod def encrypt(key, secret): if type(key) is bytes: pass if type(secret) is str: secret = secret.encode('utf-8') if type(secret) is not bytes: raise TypeError('%s: Encryption requires string or bytes' % type(secret)) return Fernet(key).encrypt(secret) @staticmethod def decrypt(key, token): return Fernet(key).decrypt(token) @staticmethod def hash(key): return hashlib.sha512(key).hexdigest() class EncryptionApi(object): @staticmethod def make_password(raw_password, salt): """10000 iterations of pbkdf2 and return only hash""" hasher = PBKDF2PasswordHasher() hashed_password = hasher.encode(raw_password, salt) return hashed_password.split('$').pop() @classmethod def encrypt(cls, raw_password, data): salt = SymmetricEncryption.generate_salt() password = cls.make_password(raw_password, salt) encryption_key = SymmetricEncryption.build_encryption_key(password) e_data = SymmetricEncryption.encrypt(encryption_key, data) return '{}${}'.format(salt, e_data.decode('utf-8')) @classmethod def decrypt(cls, raw_password, stored_data): salt, e_data = stored_data.split('$') password = cls.make_password(raw_password, salt) encryption_key = SymmetricEncryption.build_encryption_key(password) data = SymmetricEncryption.decrypt(encryption_key, e_data.encode('utf-8')) return data.decode('utf-8')