AI systems need data. But data is sensitive. This page covers legal and technical aspects of data privacy in AI systems.


1. GDPR Compliance for AI

1.1 Nine Core GDPR Principles

1. Lawfulness
   - Processing must have legal basis
   - Consent, Contract, Legal Obligation, Vital Interests, Public Task, Legitimate Interest

2. Fairness
   - Processing must not wrongfully disadvantage
   - Transparency about data usage

3. Transparency
   - Users must know what data is processed
   - Privacy statement required

4. Purpose Limitation
   - Data can only be used for original purpose
   - Cannot be repurposed later

5. Data Minimization
   - Only collect/keep necessary data
   - Less data = less risk

6. Accuracy
   - Data must be correct
   - Must have process for corrections

7. Storage Limitation
   - Don't store longer than necessary
   - Delete or anonymize after purpose fulfilled

8. Integrity & Confidentiality
   - Protection against misuse, loss, unauthorized access
   - Encryption, access controls, auditing

9. Accountability
   - Prove GDPR compliance
   - Records of processing (DPA), contracts (DPA), impact assessments

2. Anonymization & Pseudonymization

2.1 Anonymization Techniques

import hashlib
import secrets
from typing import List, Dict
from collections import Counter

class AnonymizationEngine:
    """Tools for data anonymization"""

    @staticmethod
    def irreversible_hash(value: str, salt: str = None) -> str:
        """Irreversible hashing (true anonymization)"""
        if salt is None:
            salt = secrets.token_hex(16)

        combined = f"{value}{salt}".encode('utf-8')
        hashed = hashlib.sha256(combined).hexdigest()
        return hashed[:16]

    @staticmethod
    def k_anonymity_check(dataset: List[Dict], quasi_identifiers: List[str], k: int = 5) -> bool:
        """
        K-Anonymity Check: Each quasi-identifier combination appears >= k times
        Prevents re-identification

        Args:
            dataset: List of records
            quasi_identifiers: Columns that could identify
            k: Minimum frequency
        """
        combinations = []
        for record in dataset:
            combo = tuple(record.get(qi) for qi in quasi_identifiers)
            combinations.append(combo)

        counter = Counter(combinations)
        return all(count >= k for count in counter.values())

# Usage:
engine = AnonymizationEngine()

dataset = [
    {"age": "25-30", "gender": "M", "zip": "10001"},
    {"age": "25-30", "gender": "M", "zip": "10001"},
    {"age": "25-30", "gender": "M", "zip": "10001"},
    {"age": "25-30", "gender": "M", "zip": "10001"},
    {"age": "25-30", "gender": "M", "zip": "10001"},
]
is_kanon = engine.k_anonymity_check(dataset, ["age", "gender"], k=5)
print(f"5-anonymity satisfied: {is_kanon}")

2.2 Differential Privacy

import numpy as np

class DifferentialPrivacyEngine:
    """Differential Privacy for ML"""

    @staticmethod
    def laplace_mechanism(value: float, sensitivity: float, epsilon: float) -> float:
        """
        Laplace Mechanism: Add Laplace-distributed noise

        Args:
            value: True value (e.g., average)
            sensitivity: Maximum change when removing one row
            epsilon: Privacy budget (higher = less privacy, more accuracy)
        """
        scale = sensitivity / epsilon
        noise = np.random.laplace(loc=0, scale=scale)
        return value + noise

# Usage:
engine = DifferentialPrivacyEngine()

true_value = 50000
sensitivity = 100000
epsilon = 1.0

noisy = engine.laplace_mechanism(true_value, sensitivity, epsilon)
print(f"True value: {true_value}")
print(f"DP value: {noisy:.2f}")

3. On-Premise vs Cloud: Data Residency

class DataResidencyManager:
    """Manages data location compliance"""

    COMPLIANT_REGIONS = {
        "EU": ["eu-west-1", "eu-central-1", "eu-north-1"],
        "US": ["us-east-1", "us-west-2"],
    }

    def __init__(self, required_regions: List[str]):
        self.required_regions = required_regions
        self.audit_log = []

    def store_data(self, data: str, region: str) -> bool:
        """Stores data only in compliant regions"""
        allowed = False
        for req_region in self.required_regions:
            if region in self.COMPLIANT_REGIONS.get(req_region, []):
                allowed = True
                break

        self.audit_log.append({
            "action": "store",
            "region": region,
            "status": "ALLOWED" if allowed else "DENIED"
        })

        return allowed

# Usage:
manager = DataResidencyManager(required_regions=["EU"])

ok = manager.store_data("customer data", region="eu-west-1")
print(f"Store in EU: {ok}")  # True

not_ok = manager.store_data("customer data", region="us-east-1")
print(f"Store in US: {not_ok}")  # False

4. Encryption at Rest & In Transit

from cryptography.fernet import Fernet
import ssl

class DataEncryption:
    """Encryption for data"""

    @staticmethod
    def encrypt_at_rest(data: str, key: bytes) -> str:
        """Encrypts data for storage"""
        cipher = Fernet(key)
        encrypted = cipher.encrypt(data.encode('utf-8'))
        return encrypted.decode('utf-8')

    @staticmethod
    def decrypt_at_rest(encrypted_data: str, key: bytes) -> str:
        """Decrypts stored data"""
        cipher = Fernet(key)
        decrypted = cipher.decrypt(encrypted_data.encode('utf-8'))
        return decrypted.decode('utf-8')

    @staticmethod
    def setup_tls_for_transit():
        """SSL/TLS Configuration for Data in Transit"""
        context = ssl.create_default_context()
        context.check_hostname = True
        context.verify_mode = ssl.CERT_REQUIRED
        context.minimum_version = ssl.TLSVersion.TLSv1_2
        return context

# Usage:
key = Fernet.generate_key()
encryption = DataEncryption()

plaintext = "Sensitive customer data"
encrypted = encryption.encrypt_at_rest(plaintext, key)
decrypted = encryption.decrypt_at_rest(encrypted, key)

print(f"Encrypted: {encrypted}")
print(f"Decrypted: {decrypted}")