# Irene Cipher - Best Practices Guide

## 🎯 Password Management

### Strong Passwords

**DO** ✅
```python
# Use a password manager to generate and store
password = "Kx9$mP2vL@nQ7wR4zY8jT5cE1aD6sF3h"

# Or use environment variables
import os
password = os.environ.get("ENCRYPTION_PASSWORD")

# Or prompt user securely
import getpass
password = getpass.getpass("Enter encryption password: ")
```

**DON'T** ❌
```python
# Weak passwords
password = "password123"
password = "admin"
password = "12345678"

# Hardcoded in source
password = "my_secret_password"  # Anyone with code access can see this
```

### Password Requirements

Minimum recommendations:
- **Length**: 16+ characters
- **Complexity**: Mix of uppercase, lowercase, numbers, symbols
- **Uniqueness**: Different password for each use case
- **Randomness**: Use password generator, not personal info

**Example Strong Passwords**:
```
Tr0ub4dor&3-RobustElephant
K9#mP7@nQ2$wR5!zY8^jT1%c
qX8$vL3@mP9#nQ5&wR2^zY7!
```

## 🔐 Secure Usage Patterns

### Text Encryption

```python
import irene_cipher
import getpass

def secure_encrypt_text(plaintext: str) -> tuple[bytes, str]:
    """
    Securely encrypt text with user-provided password.
    
    Returns (ciphertext, password_hash) for verification.
    """
    password = getpass.getpass("Enter encryption password: ")
    confirm = getpass.getpass("Confirm password: ")
    
    if password != confirm:
        raise ValueError("Passwords don't match")
    
    if len(password) < 16:
        raise ValueError("Password must be at least 16 characters")
    
    ciphertext = irene_cipher.encrypt_text(plaintext, password)
    
    # Optional: Store password hash for verification (NOT the password itself)
    import hashlib
    password_hash = hashlib.sha256(password.encode()).hexdigest()
    
    return ciphertext, password_hash


def secure_decrypt_text(ciphertext: bytes) -> str:
    """
    Securely decrypt text with user-provided password.
    """
    password = getpass.getpass("Enter decryption password: ")
    
    try:
        plaintext = irene_cipher.decrypt_text(ciphertext, password)
        return plaintext
    except ValueError as e:
        raise ValueError("Decryption failed: wrong password or tampered data") from e
```

### File Encryption with Progress

```python
import irene_cipher
from pathlib import Path


def encrypt_file_with_progress(input_path: str, password: str):
    """
    Encrypt file and show progress.
    """
    input_file = Path(input_path)
    output_file = input_file.with_suffix(input_file.suffix + '.enc')
    
    print(f"Encrypting {input_file.name}...")
    file_size = input_file.stat().st_size
    print(f"Size: {file_size:,} bytes ({file_size / 1024 / 1024:.2f} MB)")
    
    import time
    start = time.time()
    
    irene_cipher.encrypt_file(str(input_file), str(output_file), password)
    
    elapsed = time.time() - start
    throughput = file_size / elapsed / 1024 / 1024
    
    print(f"✓ Encrypted in {elapsed:.2f}s ({throughput:.2f} MB/s)")
    print(f"Output: {output_file}")
    
    return str(output_file)
```

### Batch File Encryption

```python
import irene_cipher
from pathlib import Path
from typing import List


def encrypt_directory(directory: str, password: str, pattern: str = "*"):
    """
    Encrypt all files in a directory matching pattern.
    """
    dir_path = Path(directory)
    files = list(dir_path.glob(pattern))
    
    print(f"Found {len(files)} files to encrypt")
    
    encrypted_files = []
    for i, file_path in enumerate(files, 1):
        if file_path.is_file():
            print(f"[{i}/{len(files)}] Encrypting {file_path.name}...")
            output_path = str(file_path) + ".enc"
            
            try:
                irene_cipher.encrypt_file(str(file_path), output_path, password)
                encrypted_files.append(output_path)
                print(f"  ✓ {output_path}")
            except Exception as e:
                print(f"  ✗ Failed: {e}")
    
    print(f"\n✓ Encrypted {len(encrypted_files)}/{len(files)} files")
    return encrypted_files
```

## 🛡️ Error Handling

### Proper Error Handling

```python
import irene_cipher
import logging

logger = logging.getLogger(__name__)


def decrypt_with_proper_error_handling(ciphertext: bytes, password: str) -> str:
    """
    Decrypt with comprehensive error handling.
    """
    try:
        plaintext = irene_cipher.decrypt_text(ciphertext, password)
        return plaintext
    
    except ValueError as e:
        # Authentication failed - wrong password or tampered data
        logger.error(f"Authentication failed: {e}")
        
        # In production, distinguish between:
        # 1. Wrong password (user error, retry allowed)
        # 2. Tampered data (security event, log and alert)
        
        # For now, treat both as authentication failure
        raise ValueError("Decryption failed: check password and data integrity") from e
    
    except RuntimeError as e:
        # Other runtime errors (I/O, etc.)
        logger.error(f"Runtime error during decryption: {e}")
        raise
    
    except Exception as e:
        # Unexpected errors
        logger.exception(f"Unexpected error during decryption: {e}")
        raise
```

### File Operation Error Handling

```python
import irene_cipher
from pathlib import Path


def safe_decrypt_file(encrypted_path: str, password: str) -> str:
    """
    Safely decrypt file with comprehensive error handling.
    """
    encrypted_file = Path(encrypted_path)
    
    # Validate input
    if not encrypted_file.exists():
        raise FileNotFoundError(f"Encrypted file not found: {encrypted_path}")
    
    if not encrypted_file.is_file():
        raise ValueError(f"Not a file: {encrypted_path}")
    
    # Determine output path (remove .enc extension)
    if encrypted_file.suffix == '.enc':
        output_file = encrypted_file.with_suffix('')
    else:
        output_file = encrypted_file.with_name(encrypted_file.name + '.decrypted')
    
    # Check if output exists
    if output_file.exists():
        raise FileExistsError(f"Output file already exists: {output_file}")
    
    # Decrypt
    try:
        irene_cipher.decrypt_file(str(encrypted_file), str(output_file), password)
        return str(output_file)
    
    except ValueError as e:
        # Authentication failed - clean up partial output
        if output_file.exists():
            output_file.unlink()
        raise ValueError(f"Decryption failed: {e}") from e
    
    except Exception as e:
        # Other errors - clean up partial output
        if output_file.exists():
            output_file.unlink()
        raise
```

## 💾 Secure Storage

### Storing Encrypted Data

**DO** ✅
```python
# Store ciphertext safely
ciphertext = irene_cipher.encrypt_text(secret, password)

# Write to file with proper permissions (Unix)
import os
with open("encrypted.bin", "wb") as f:
    f.write(ciphertext)
os.chmod("encrypted.bin", 0o600)  # Owner read/write only

# Or use secure storage APIs
# - Windows: DPAPI
# - macOS: Keychain
# - Linux: Secret Service API
```

**DON'T** ❌
```python
# Don't store password with ciphertext
with open("data.json", "w") as f:
    json.dump({
        "ciphertext": ciphertext.hex(),
        "password": password  # ❌ NEVER DO THIS
    }, f)

# Don't use predictable filenames
# Bad: "secrets.enc", "passwords.enc"
# Better: Use UUIDs or hashed names
```

### Secure Key Storage

```python
import irene_cipher
import os
from pathlib import Path


def store_key_securely():
    """
    Generate and store key securely.
    """
    key = irene_cipher.generate_key()
    
    # Option 1: Environment variable (for services)
    # export ENCRYPTION_KEY=<base64_key>
    
    # Option 2: Secure file with restricted permissions
    key_file = Path.home() / ".config" / "myapp" / "key.bin"
    key_file.parent.mkdir(parents=True, exist_ok=True)
    
    key_file.write_bytes(key)
    os.chmod(key_file, 0o600)  # Unix only
    
    # Option 3: OS keyring (most secure)
    # Use keyring library: pip install keyring
    # import keyring
    # keyring.set_password("myapp", "encryption_key", key.hex())
    
    return key
```

## 🔄 Backup and Recovery

### Backup Best Practices

```python
import irene_cipher
import shutil
from pathlib import Path
from datetime import datetime


def backup_encrypted_file(file_path: str, backup_dir: str):
    """
    Create timestamped backup of encrypted file.
    """
    source = Path(file_path)
    backup_path = Path(backup_dir)
    backup_path.mkdir(parents=True, exist_ok=True)
    
    # Create timestamped backup
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_file = backup_path / f"{source.stem}_{timestamp}{source.suffix}"
    
    shutil.copy2(source, backup_file)
    
    # Verify backup integrity
    assert source.stat().st_size == backup_file.stat().st_size
    
    print(f"✓ Backup created: {backup_file}")
    return str(backup_file)


def verify_encrypted_backup(backup_path: str, password: str):
    """
    Verify encrypted backup can be decrypted.
    """
    import tempfile
    
    with tempfile.NamedTemporaryFile(delete=True) as tmp:
        try:
            irene_cipher.decrypt_file(backup_path, tmp.name, password)
            print(f"✓ Backup verified: {backup_path}")
            return True
        except Exception as e:
            print(f"✗ Backup verification failed: {e}")
            return False
```

## 🧪 Testing

### Testing Encrypted Data

```python
import irene_cipher
import unittest


class TestEncryption(unittest.TestCase):
    """Test encryption/decryption functionality."""
    
    def test_roundtrip(self):
        """Test encryption and decryption roundtrip."""
        plaintext = "Test message"
        password = "test_password_16chars!"
        
        ciphertext = irene_cipher.encrypt_text(plaintext, password)
        decrypted = irene_cipher.decrypt_text(ciphertext, password)
        
        self.assertEqual(decrypted, plaintext)
    
    def test_wrong_password_fails(self):
        """Test that wrong password fails."""
        ciphertext = irene_cipher.encrypt_text("secret", "correct_pass_16c!")
        
        with self.assertRaises(ValueError):
            irene_cipher.decrypt_text(ciphertext, "wrong_password_16!")
    
    def test_tampered_data_fails(self):
        """Test that tampered data fails."""
        ciphertext = irene_cipher.encrypt_text("data", "password_16chars!")
        
        # Tamper with ciphertext
        tampered = bytearray(ciphertext)
        tampered[-1] ^= 0xFF
        
        with self.assertRaises(ValueError):
            irene_cipher.decrypt_text(bytes(tampered), "password_16chars!")
```

## 📊 Performance Optimization

### Optimizing File Operations

```python
import irene_cipher
from pathlib import Path


def encrypt_large_file_optimized(input_path: str, password: str):
    """
    Encrypt large file with performance considerations.
    """
    input_file = Path(input_path)
    output_file = input_file.with_suffix(input_file.suffix + '.enc')
    
    # Pre-check available disk space
    file_size = input_file.stat().st_size
    free_space = shutil.disk_usage(output_file.parent).free
    
    # Encrypted file is slightly larger due to headers and tags
    estimated_size = file_size * 1.02 + 1024 * 1024  # ~2% overhead + 1MB margin
    
    if free_space < estimated_size:
        raise IOError(f"Insufficient disk space: need {estimated_size:,} bytes")
    
    # Encrypt (library handles streaming automatically)
    irene_cipher.encrypt_file(str(input_file), str(output_file), password)
    
    return str(output_file)
```

## 🔍 Debugging

### Debug Mode

```python
import irene_cipher
import logging

# Enable detailed logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)


def encrypt_with_logging(plaintext: str, password: str) -> bytes:
    """
    Encrypt with detailed logging for debugging.
    """
    logger.debug(f"Encrypting {len(plaintext)} bytes")
    logger.debug(f"Password length: {len(password)}")
    
    try:
        ciphertext = irene_cipher.encrypt_text(plaintext, password)
        logger.debug(f"Encryption successful, ciphertext length: {len(ciphertext)}")
        return ciphertext
    
    except Exception as e:
        logger.exception(f"Encryption failed: {e}")
        raise
```

## 🎓 Common Patterns

### Password-Protected Archive

```python
import irene_cipher
import zipfile
import tempfile
from pathlib import Path


def create_encrypted_archive(files: list[str], output: str, password: str):
    """
    Create encrypted archive of multiple files.
    """
    # Create temporary zip file
    with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmp:
        tmp_zip = tmp.name
    
    # Add files to zip
    with zipfile.ZipFile(tmp_zip, 'w', zipfile.ZIP_DEFLATED) as zf:
        for file_path in files:
            zf.write(file_path, Path(file_path).name)
    
    # Encrypt the zip file
    irene_cipher.encrypt_file(tmp_zip, output, password)
    
    # Clean up temporary file
    Path(tmp_zip).unlink()
    
    print(f"✓ Encrypted archive created: {output}")
```

---

**Remember**: Security is only as strong as your weakest link. Use strong passwords, handle errors properly, and keep your software updated!
