# Path: djenga/encryption/helpers.py
# def _as_bytes(value):
#     if isinstance(value, six.string_types):
#         value = value.encode('utf-8')
#     return value
# 
# Path: djenga/encryption/helpers.py
# def from_b64_str(value: str):
#     value = value.encode('utf-8')
#     return b64decode(value)
# 
# Path: djenga/encryption/helpers.py
# def _get_client(region: str = None, profile: str = None):
#     key = f'{region}-{profile}'
#     client = thread_local.sessions.get(key)
#     if not client:
#         session = boto3.Session(region_name=region, profile_name=profile)
#         client = session.client('kms')
#         thread_local.sessions[key] = client
#     return client
# 
# Path: djenga/encryption/helpers.py
# def _prefix_alias(alias: str):
#     if not alias.startswith('alias/'):
#         alias = f'alias/{alias}'
#     return alias
# 
# Path: djenga/encryption/gcm.py
# def encrypt_bytes(plain_text, key=None, auth_header='djenga'):
#     """
#     The encrypt function encrypts a unicode string using the
#     Blowfish cipher (provided by pycrypto).  The key used is
#     the SECRET_KEY specified in the settings file.
# 
#     :param plain_text: The plaintext unicode string to be encrypted.
#     :param key: the password to use for encryption
#     :param auth_header: str
#     :return The encrypted ciphertext.
# 
#     """
#     kdf_salt = get_random_bytes(32)
#     key = key or settings.DJENGA_ENCRYPTION_KEY
#     derived_key = derive_key(key, kdf_salt, 32)
#     cipher = AES.new(derived_key, AES.MODE_GCM)
#     auth_header = _as_bytes(auth_header)
#     cipher.update(auth_header)
#     cipher_text, tag = cipher.encrypt_and_digest(plain_text)
#     nonce = cipher.nonce
#     return _gcm_pack(auth_header, cipher_text, tag, nonce, kdf_salt)
# 
# Path: djenga/encryption/gcm.py
# def decrypt_bytes(packed_value, key=None):
#     """
#     The decrypt function decrypts unicode strings that have
#     been encrypted by the util.encryption.encrypt() function.
#     The cipher used is Blowfish (provided by pcrypto), and the
#     key used is the SECRET_KEY specified in the settings file.
# 
#     :param packed_value: The encrypted pieces needed for decryption.
#     :param key: the password to use when encrypting
#     :return The decrypted plaintext (unicode) string.
# 
#     >>> import uuid
#     >>> key = uuid.uuid4()
#     >>> st = 'hello'
#     >>> decrypt(encrypt(st, key.bytes), key.bytes) == st
#     True
#     """
#     header, cipher_text, tag, nonce, kdf_salt = _gcm_unpack(packed_value)
#     key = key or settings.DJENGA_ENCRYPTION_KEY
#     decryption_key = derive_key(key, kdf_salt, 32)
#     cipher = AES.new(decryption_key, AES.MODE_GCM, nonce)
#     cipher.update(header)
#     data = cipher.decrypt_and_verify(cipher_text, tag)
#     return data


from .helpers import _as_bytes
from .helpers import from_b64_str
from .helpers import _get_client
from .helpers import _prefix_alias
from .gcm import encrypt_bytes as gcm_encrypt
from .gcm import decrypt_bytes as gcm_decrypt

def encrypt_bytes(
        plain_text: bytes
        , alias: str
        , region: str = None
        , profile: str = None) -> str:
    client = _get_client(region, profile)
    alias = _prefix_alias(alias)
    response = client.generate_data_key(KeyId=alias, KeySpec='AES_256')
    data_key = response['Plaintext']
    header = response['CiphertextBlob']
    value = gcm_encrypt(plain_text, data_key, auth_header=header)
    return value


def decrypt_bytes(
        packed_value: str
        , region: str = None
        , profile: str = None) -> bytes:
    pieces = packed_value.split('|', 1)
    wrapped_data_key = pieces[0]
    wrapped_data_key = from_b64_str(wrapped_data_key)
    client = _get_client(region, profile)
    response = client.decrypt(CiphertextBlob=wrapped_data_key)
    data_key = response['Plaintext']
    plain_text = gcm_decrypt(packed_value, data_key)
    return plain_text


def encrypt(plain_text, alias, region: str = None, profile: str = None) -> str:

# Path: djenga/encryption/helpers.py
# def _as_bytes(value):
#     if isinstance(value, six.string_types):
#         value = value.encode('utf-8')
#     return value
# 
# Path: djenga/encryption/helpers.py
# def from_b64_str(value: str):
#     value = value.encode('utf-8')
#     return b64decode(value)
# 
# Path: djenga/encryption/helpers.py
# def _get_client(region: str = None, profile: str = None):
#     key = f'{region}-{profile}'
#     client = thread_local.sessions.get(key)
#     if not client:
#         session = boto3.Session(region_name=region, profile_name=profile)
#         client = session.client('kms')
#         thread_local.sessions[key] = client
#     return client
# 
# Path: djenga/encryption/helpers.py
# def _prefix_alias(alias: str):
#     if not alias.startswith('alias/'):
#         alias = f'alias/{alias}'
#     return alias
# 
# Path: djenga/encryption/gcm.py
# def encrypt_bytes(plain_text, key=None, auth_header='djenga'):
#     """
#     The encrypt function encrypts a unicode string using the
#     Blowfish cipher (provided by pycrypto).  The key used is
#     the SECRET_KEY specified in the settings file.
# 
#     :param plain_text: The plaintext unicode string to be encrypted.
#     :param key: the password to use for encryption
#     :param auth_header: str
#     :return The encrypted ciphertext.
# 
#     """
#     kdf_salt = get_random_bytes(32)
#     key = key or settings.DJENGA_ENCRYPTION_KEY
#     derived_key = derive_key(key, kdf_salt, 32)
#     cipher = AES.new(derived_key, AES.MODE_GCM)
#     auth_header = _as_bytes(auth_header)
#     cipher.update(auth_header)
#     cipher_text, tag = cipher.encrypt_and_digest(plain_text)
#     nonce = cipher.nonce
#     return _gcm_pack(auth_header, cipher_text, tag, nonce, kdf_salt)
# 
# Path: djenga/encryption/gcm.py
# def decrypt_bytes(packed_value, key=None):
#     """
#     The decrypt function decrypts unicode strings that have
#     been encrypted by the util.encryption.encrypt() function.
#     The cipher used is Blowfish (provided by pcrypto), and the
#     key used is the SECRET_KEY specified in the settings file.
# 
#     :param packed_value: The encrypted pieces needed for decryption.
#     :param key: the password to use when encrypting
#     :return The decrypted plaintext (unicode) string.
# 
#     >>> import uuid
#     >>> key = uuid.uuid4()
#     >>> st = 'hello'
#     >>> decrypt(encrypt(st, key.bytes), key.bytes) == st
#     True
#     """
#     header, cipher_text, tag, nonce, kdf_salt = _gcm_unpack(packed_value)
#     key = key or settings.DJENGA_ENCRYPTION_KEY
#     decryption_key = derive_key(key, kdf_salt, 32)
#     cipher = AES.new(decryption_key, AES.MODE_GCM, nonce)
#     cipher.update(header)
#     data = cipher.decrypt_and_verify(cipher_text, tag)
#     return data


# Path: djenga/encryption/kms_wrapped.py
from .helpers import _as_bytes
from .helpers import from_b64_str
from .helpers import _get_client
from .helpers import _prefix_alias
from .gcm import encrypt_bytes as gcm_encrypt
from .gcm import decrypt_bytes as gcm_decrypt


def encrypt_bytes(
        plain_text: bytes
        , alias: str
        , region: str = None
        , profile: str = None) -> str:
    client = _get_client(region, profile)
    alias = _prefix_alias(alias)
    response = client.generate_data_key(KeyId=alias, KeySpec='AES_256')
    data_key = response['Plaintext']
    header = response['CiphertextBlob']
    value = gcm_encrypt(plain_text, data_key, auth_header=header)
    return value


def decrypt_bytes(
        packed_value: str
        , region: str = None
        , profile: str = None) -> bytes:
    pieces = packed_value.split('|', 1)
    wrapped_data_key = pieces[0]
    wrapped_data_key = from_b64_str(wrapped_data_key)
    client = _get_client(region, profile)
    response = client.decrypt(CiphertextBlob=wrapped_data_key)
    data_key = response['Plaintext']
    plain_text = gcm_decrypt(packed_value, data_key)
    return plain_text


def encrypt(plain_text, alias, region: str = None, profile: str = None) -> str:

    plain_text = _as_bytes(plain_text)