import ssl

def create_tls_context(purpose=ssl.Purpose.CLIENT_AUTH):
    """
    Creates an SSL context configured for TLS 1.3.

    Args:
        purpose: The SSL purpose (e.g., CLIENT_AUTH, SERVER_AUTH).

    Returns:
        An SSLContext object configured for TLS 1.3.
    """
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT if purpose == ssl.Purpose.CLIENT_AUTH else ssl.PROTOCOL_TLS_SERVER)

    # Require TLS 1.3
    context.minimum_version = ssl.TLSVersion.TLSv1_3

    # TLS 1.3 cipher suites are handled automatically by the underlying SSL library
    # when minimum_version is set to TLSv1_3. Explicitly setting them via
    # set_ciphers can cause issues. The required suites (AES-256-GCM, CHACHA20)
    # are typically included and preferred by default in modern OpenSSL.
    # context.set_ciphers('...') # Removed

    # Configure certificate loading and verification
    if purpose == ssl.Purpose.SERVER_AUTH:
        # Server context: Load server cert/key and require client certs for RBAC
        # context.load_cert_chain(certfile="path/to/server_cert.pem", keyfile="path/to/server_key.pem") # Placeholder: Needs actual paths
        context.verify_mode = ssl.CERT_REQUIRED
        # context.load_verify_locations(cafile="path/to/trusted_client_ca.pem") # Placeholder: Needs actual CA path for client cert validation
    elif purpose == ssl.Purpose.CLIENT_AUTH:
        # Client context: Load client cert/key and verify server cert against CA
        # context.load_cert_chain(certfile="path/to/client_cert.pem", keyfile="path/to/client_key.pem") # Placeholder: Needs actual paths
        # context.load_verify_locations(cafile="path/to/trusted_server_ca.pem") # Placeholder: Needs actual CA path
        context.verify_mode = ssl.CERT_REQUIRED # Verify the server certificate

    # minimum_version = TLSv1_3 implicitly disables older protocols.
    # Explicit OP_NO flags are redundant but harmless; removed for clarity.
    # context.options |= ssl.OP_NO_SSLv2 # Redundant
    # context.options |= ssl.OP_NO_SSLv3 # Redundant
    # context.options |= ssl.OP_NO_TLSv1 # Redundant
    # context.options |= ssl.OP_NO_TLSv1_1 # Redundant
    # context.options |= ssl.OP_NO_TLSv1_2 # Redundant

    # Options for perfect forward secrecy are generally enabled by default with TLS 1.3 ciphers
    # context.options |= ssl.OP_SINGLE_DH_USE
    # context.options |= ssl.OP_SINGLE_ECDH_USE

    # Ensure TLS 1.3 is the minimum (already set, but good to be explicit)
    context.minimum_version = ssl.TLSVersion.TLSv1_3

    return context

# Example usage (can be removed or kept for demonstration)
if __name__ == '__main__':
    client_context = create_tls_context(ssl.Purpose.CLIENT_AUTH)
    print(f"Client Context Minimum TLS Version: {client_context.minimum_version}")
    # print(f"Client Context Ciphers: {client_context.get_ciphers()}") # Requires OpenSSL 1.1.1+

    server_context = create_tls_context(ssl.Purpose.SERVER_AUTH)
    print(f"Server Context Minimum TLS Version: {server_context.minimum_version}")
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os

class AES256Cipher:
    """AES-256-GCM encryption/decryption wrapper class."""
    
    def __init__(self, key: bytes = None):
        """
        Initialize cipher with optional key.
        
        Args:
            key: Optional 32-byte AES-256 key. If None, generates new key.
        """
        self.key = key if key is not None else self.generate_key()
    
    @staticmethod
    def generate_key() -> bytes:
        """Generate a secure 256-bit AES key for encryption/decryption.
        
        Returns:
            bytes: 32-byte AES-256 key
        """
        return os.urandom(32)
    
    def encrypt(self, plaintext: bytes) -> bytes:
        """Encrypt data using AES-256-GCM.
        
        Args:
            plaintext: Data to encrypt
            
        Returns:
            bytes: Encrypted data in format (nonce + ciphertext + tag)
        """
        return encrypt_data(plaintext, self.key)
    
    def decrypt(self, encrypted_data: bytes) -> bytes:
        """Decrypt data using AES-256-GCM.
        
        Args:
            encrypted_data: Data in format (nonce + ciphertext + tag)
            
        Returns:
            bytes: Decrypted plaintext
        """
        return decrypt_data(encrypted_data, self.key)

def generate_key():
    """Generate a secure 256-bit AES key for encryption/decryption.
    
    Returns:
        bytes: 32-byte AES-256 key
    """
    return os.urandom(32)

def encrypt_data(plaintext: bytes, key: bytes) -> bytes:
    """Encrypt data using AES-256-GCM.
    
    Args:
        plaintext: Data to encrypt
        key: 32-byte AES-256 key
        
    Returns:
        bytes: Encrypted data in format (nonce + ciphertext + tag)
        
    Raises:
        ValueError: If key length is invalid
    """
    if len(key) != 32:
        raise ValueError("Key must be 32 bytes for AES-256")
        
    # Generate random 96-bit nonce
    nonce = os.urandom(12)
    
    # Create cipher and encrypt
    cipher = Cipher(
        algorithms.AES(key),
        modes.GCM(nonce),
        backend=default_backend()
    )
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    
    # Return nonce + ciphertext + tag
    return nonce + ciphertext + encryptor.tag

def decrypt_data(encrypted_data: bytes, key: bytes) -> bytes:
    """Decrypt data using AES-256-GCM.
    
    Args:
        encrypted_data: Data in format (nonce + ciphertext + tag)
        key: 32-byte AES-256 key
        
    Returns:
        bytes: Decrypted plaintext
        
    Raises:
        ValueError: If key length is invalid or data is malformed
    """
    if len(key) != 32:
        raise ValueError("Key must be 32 bytes for AES-256")
    if len(encrypted_data) < 28:  # Minimum: 12 nonce + 16 tag
        raise ValueError("Encrypted data too short")
        
    # Split into components
    nonce = encrypted_data[:12]
    ciphertext = encrypted_data[12:-16]
    tag = encrypted_data[-16:]
    
    # Create cipher and decrypt
    cipher = Cipher(
        algorithms.AES(key),
        modes.GCM(nonce, tag),
        backend=default_backend()
    )
    decryptor = cipher.decryptor()
    return decryptor.update(ciphertext) + decryptor.finalize()

# Example usage for AES-256-GCM functions
if __name__ == '__main__':
    # Generate key
    key = generate_key()
    print(f"Generated AES-256 key: {key.hex()}")
    
    # Encrypt test data
    plaintext = b"Test message for AES-256-GCM implementation"
    encrypted = encrypt_data(plaintext, key)
    print(f"Encrypted data (hex): {encrypted.hex()}")
    
    # Decrypt test data
    decrypted = decrypt_data(encrypted, key)
    print(f"Decrypted data: {decrypted.decode()}")
    # print(f"Server Context Ciphers: {server_context.get_ciphers()}") # Requires OpenSSL 1.1.1+