# AES Encryption Module

A comprehensive, secure, and easy-to-use AES encryption implementation in Python following Google's Python Style Guide and security best practices.

## Features

- **Multiple AES Modes**: Support for CBC, GCM, and CTR encryption modes
- **Flexible Key Management**: Direct key input, password-based key derivation (PBKDF2), and secure key generation
- **Multiple Key Sizes**: Support for AES-128, AES-192, and AES-256
- **Secure Defaults**: Cryptographically secure random number generation for keys, IVs, and salts
- **Easy-to-Use API**: Both class-based and functional interfaces
- **Comprehensive Error Handling**: Detailed error messages and proper exception hierarchy
- **Base64 Encoding**: Built-in support for base64 encoding/decoding
- **Security-First Design**: Follows cryptographic best practices

## Installation

Install the required dependencies:

```bash
pip install -r requirements.txt
```

The main dependency is the `cryptography` library, which provides secure, well-tested cryptographic primitives.

## Quick Start

### Simple String Encryption

```python
from aes_encryption import encrypt_string, decrypt_string

# Encrypt a string with a password
password = "my_secure_password"
message = "This is a secret message"

encrypted = encrypt_string(message, password)
decrypted = decrypt_string(encrypted, password)

print(f"Original: {message}")
print(f"Decrypted: {decrypted}")
```

### Using the AESEncryption Class

```python
from aes_encryption import AESEncryption

# Create encryptor with generated key
encryptor = AESEncryption(mode='GCM')
key = encryptor.generate_key()  # 256-bit key by default

# Encrypt data
message = "Confidential information"
ciphertext, iv = encryptor.encrypt(message)

# Decrypt data
decrypted = encryptor.decrypt(ciphertext, iv)
print(decrypted.decode('utf-8'))
```

### Password-Based Encryption

```python
from aes_encryption import AESEncryption

encryptor = AESEncryption()
password = "strong_password_123"

# Derive key from password
key, salt = encryptor.derive_key_from_password(password)

# Encrypt with derived key
ciphertext, iv = encryptor.encrypt("Secret data")
decrypted = encryptor.decrypt(ciphertext, iv)
```

## API Reference

### AESEncryption Class

#### Constructor

```python
AESEncryption(key=None, mode='CBC')
```

- `key` (bytes, optional): Encryption key (16, 24, or 32 bytes)
- `mode` (str): Encryption mode ('CBC', 'GCM', 'CTR')

#### Methods

##### Key Management

- `set_key(key: bytes)`: Set encryption key
- `generate_key(key_size: int = 32) -> bytes`: Generate random key
- `derive_key_from_password(password: str, salt: bytes = None, iterations: int = 100000, key_size: int = 32) -> Tuple[bytes, bytes]`: Derive key from password using PBKDF2

##### Encryption/Decryption

- `encrypt(plaintext: Union[str, bytes], iv: bytes = None) -> Tuple[bytes, bytes]`: Encrypt data
- `decrypt(ciphertext: bytes, iv: bytes) -> bytes`: Decrypt data
- `encrypt_to_base64(plaintext: Union[str, bytes], iv: bytes = None) -> str`: Encrypt and encode as base64
- `decrypt_from_base64(encoded_data: str) -> bytes`: Decrypt base64-encoded data

##### Utility

- `get_key_info() -> dict`: Get information about current key

### Convenience Functions

- `create_aes_encryptor(password: str = None, key: bytes = None, mode: str = 'CBC') -> AESEncryption`: Factory function
- `encrypt_string(plaintext: str, password: str, mode: str = 'CBC') -> str`: Simple string encryption
- `decrypt_string(encrypted_data: str, password: str, mode: str = 'CBC') -> str`: Simple string decryption

## Encryption Modes

### CBC (Cipher Block Chaining)
- **Default mode**
- Requires padding (PKCS7)
- Sequential encryption (not parallelizable)
- Good for general-purpose encryption

### GCM (Galois/Counter Mode)
- Authenticated encryption
- No padding required
- Parallelizable
- Provides both confidentiality and authenticity

### CTR (Counter Mode)
- Stream cipher mode
- No padding required
- Parallelizable
- Fast encryption/decryption

## Security Features

### Secure Random Generation
- Uses `secrets` module for cryptographically secure randomness
- Random IVs for each encryption operation
- Random salts for password derivation

### Key Derivation
- PBKDF2 with SHA-256
- Default 100,000 iterations (configurable)
- Random salt generation
- Supports custom iteration counts for enhanced security

### Error Handling
- Custom exception hierarchy
- Secure error messages (no sensitive data leakage)
- Input validation and sanitization

## Examples

### File Encryption

```python
import json
from aes_encryption import create_aes_encryptor

# Encrypt a file
password = "file_encryption_password"
encryptor = create_aes_encryptor(password=password)

# Read and encrypt file
with open('document.txt', 'rb') as f:
    file_data = f.read()

encrypted_data, iv = encryptor.encrypt(file_data)

# Save encrypted file with metadata
metadata = {'iv': iv.hex(), 'mode': 'CBC'}
with open('document.txt.encrypted', 'wb') as f:
    metadata_json = json.dumps(metadata).encode('utf-8')
    f.write(len(metadata_json).to_bytes(4, 'big'))
    f.write(metadata_json)
    f.write(encrypted_data)
```

### Binary Data Encryption

```python
from aes_encryption import AESEncryption
import os

# Generate random binary data
binary_data = os.urandom(1024)

encryptor = AESEncryption()
key = encryptor.generate_key()

# Encrypt binary data
ciphertext, iv = encryptor.encrypt(binary_data)
decrypted_data = encryptor.decrypt(ciphertext, iv)

assert binary_data == decrypted_data
```

### Multiple Key Sizes

```python
from aes_encryption import AESEncryption

# Test different AES key sizes
key_sizes = [16, 24, 32]  # AES-128, AES-192, AES-256

for size in key_sizes:
    encryptor = AESEncryption()
    key = encryptor.generate_key(size)
    
    info = encryptor.get_key_info()
    print(f"Using AES-{info['key_size_bits']}")
    
    # Test encryption
    ciphertext, iv = encryptor.encrypt("Test message")
    decrypted = encryptor.decrypt(ciphertext, iv)
```

## Testing

Run the comprehensive test suite:

```bash
python test_aes_encryption.py
```

Run the demonstration script:

```bash
python aes_demo.py
```

## Performance

The module is optimized for both security and performance:

- Uses the `cryptography` library's optimized implementations
- Efficient memory usage
- Minimal overhead for small and large data
- Benchmarking included in demo script

Typical performance (varies by system):
- Small data (1KB): ~1ms encryption/decryption
- Medium data (100KB): ~10ms encryption/decryption  
- Large data (1MB): ~50ms encryption/decryption

## Security Considerations

### Best Practices Implemented

1. **Secure Random Generation**: All random values (keys, IVs, salts) use cryptographically secure generators
2. **Key Derivation**: PBKDF2 with high iteration counts and random salts
3. **No Key Reuse**: Fresh IVs for every encryption operation
4. **Proper Padding**: PKCS7 padding for CBC mode
5. **Input Validation**: Comprehensive validation of all inputs
6. **Error Handling**: Secure error messages without information leakage

### Important Security Notes

- **Password Strength**: Use strong, unique passwords for key derivation
- **Key Storage**: Store encryption keys securely (consider using key management systems)
- **Salt Storage**: Store salts alongside encrypted data for decryption
- **IV Storage**: Store IVs alongside encrypted data (IVs are not secret)
- **Iteration Count**: Consider increasing PBKDF2 iterations for high-security applications

## Error Handling

The module provides a comprehensive exception hierarchy:

- `AESEncryptionError`: Base exception for all encryption-related errors
- `InvalidKeyError`: Raised for invalid keys or key-related issues
- `DecryptionError`: Raised when decryption fails

```python
from aes_encryption import AESEncryption, InvalidKeyError, DecryptionError

try:
    encryptor = AESEncryption()
    encryptor.set_key(b"invalid_key")  # Too short
except InvalidKeyError as e:
    print(f"Key error: {e}")

try:
    # Attempt decryption with wrong password
    decrypt_string(encrypted_data, "wrong_password")
except DecryptionError as e:
    print(f"Decryption failed: {e}")
```

## License

This project is licensed under the Apache License 2.0. See the license headers in the source files for details.

## Contributing

Contributions are welcome! Please ensure:

1. Follow the Google Python Style Guide
2. Add comprehensive tests for new features
3. Update documentation as needed
4. Run security scans before submitting

## Changelog

### Version 1.0.0
- Initial release
- Support for CBC, GCM, and CTR modes
- Password-based key derivation
- Comprehensive test suite
- Security-focused design