Skip to content

Transport Security Architecture

AICO implements transport security using CurveZMQ for message bus communication and plans application-level libsodium integration for enhanced security. The current implementation provides strong cryptographic guarantees while maintaining compatibility with AICO's modular architecture and deployment flexibility.

Current Status: ZeroMQ message bus uses CurveZMQ encryption. Full libsodium application-level encryption is planned for future implementation.

Architecture Overview

Design Principles

Application-Level Encryption: Uses libsodium instead of TLS for direct cryptographic control and simplified (no) certificate management.

Zero-Configuration Security: Automatic key exchange and session establishment without manual certificate setup.

Deployment Agnostic: Consistent security model across coupled (same device) and detached (separate devices) deployments.

Performance Optimized: Hardware-accelerated ChaCha20-Poly1305 with minimal overhead.

Integration Native: Leverages existing AICO key management and authentication systems.

Communication Channels

AICO's transport security protects three primary communication channels:

flowchart LR
    Frontend["Frontend<br/>(Flutter)"] 
    Backend["Backend<br/>(Python)"]
    CLI["CLI<br/>(Python)"]
    Studio["Studio<br/>(React)"]

    Frontend -.->|libsodium| Backend
    CLI -.->|libsodium| Backend  
    CLI -.->|libsodium| Studio
    Studio -.->|libsodium| Backend

    classDef secure fill:#663399,stroke:#9370DB,color:#fff
    class Frontend,Backend,CLI,Studio secure

Cryptographic Foundation

Core Algorithms

Component Algorithm Purpose
Key Exchange X25519 Establish shared secrets
Encryption XChaCha20-Poly1305 Message encryption with authentication
Key Derivation BLAKE2b Derive session keys from shared secrets
Authentication Ed25519 Component identity verification

Algorithm Selection Rationale

XChaCha20-Poly1305: - 192-bit nonces enable safe random nonce generation - No practical message count limits - Software-optimized performance (3x faster than AES without hardware acceleration) - Timing attack resistant

X25519 Key Exchange: - 128-bit security level - Fast scalar multiplication - Small key sizes (32 bytes) - Widely audited implementation

Security Architecture

Component Identity System

Each AICO component maintains a persistent Ed25519 identity keypair:

# Component identity management
class ComponentIdentity:
    def __init__(self, component_name: str, key_manager: AICOKeyManager):
        self.component_name = component_name
        self.key_manager = key_manager
        self._load_or_generate_identity()

    def _load_or_generate_identity(self):
        # Try to load existing identity
        stored_key = self.key_manager.get_component_key(self.component_name, "identity")

        if stored_key:
            self.secret_key = stored_key
            self.public_key = crypto_sign_sk_to_pk(self.secret_key)
        else:
            # Generate new identity
            self.public_key, self.secret_key = crypto_sign_keypair()
            self.key_manager.store_component_key(
                self.component_name, "identity", self.secret_key
            )

Session Key Derivation

Session keys are derived using libsodium's crypto_kx API with component identities:

class SecureChannel:
    def establish_session(self, peer_public_key: bytes) -> tuple[bytes, bytes]:
        """Establish bidirectional session keys with peer"""

        # Perform X25519 key exchange
        if self.is_client:
            rx_key, tx_key = crypto_kx_client_session_keys(
                self.identity.public_key,
                self.identity.secret_key, 
                peer_public_key
            )
        else:
            rx_key, tx_key = crypto_kx_server_session_keys(
                self.identity.public_key,
                self.identity.secret_key,
                peer_public_key
            )

        return rx_key, tx_key

Message Format

All encrypted messages use a standardized format:

┌─────────────┬──────────────┬──────────────┬─────────────────┬──────────────┐
│   Header    │    Nonce     │   Metadata   │ Encrypted Data  │   Auth Tag   │
│   8 bytes   │   24 bytes   │   Variable   │   Variable      │   16 bytes   │
└─────────────┴──────────────┴──────────────┴─────────────────┴──────────────┘

Header: AICO-MSG (8 ASCII bytes) + version Nonce: 192-bit XChaCha20 nonce (random generation safe) Metadata: Protocol-specific data (timestamps, message types) Auth Tag: Poly1305 authentication tag

Protocol Implementation

Connection Establishment

Phase 1: Identity Exchange

# 1. Components exchange public keys
handshake_msg = {
    "component": "frontend",
    "public_key": base64.encode(identity.public_key),
    "timestamp": time.time(),
    "challenge": os.urandom(32)
}

Phase 2: Authentication

# 2. Mutual authentication via Ed25519 signatures
def authenticate_peer(peer_msg: dict) -> bool:
    # Verify timestamp freshness
    if abs(time.time() - peer_msg["timestamp"]) > 30:
        return False

    # Verify signature over challenge
    signature = peer_msg["signature"]
    challenge = peer_msg["challenge"]
    peer_pubkey = base64.decode(peer_msg["public_key"])

    try:
        crypto_sign_open(signature + challenge, peer_pubkey)
        return True
    except ValueError:
        return False

Phase 3: Session Key Derivation

# 3. Derive session keys using crypto_kx
rx_key, tx_key = establish_session(peer_public_key)

# 4. Begin encrypted communication
secure_channel = SecureChannel(rx_key, tx_key)

Message Encryption

class SecureChannel:
    def encrypt_message(self, plaintext: bytes, metadata: dict = None) -> bytes:
        # Generate random nonce (safe with XChaCha20)
        nonce = os.urandom(24)

        # Serialize metadata as additional data
        additional_data = json.dumps(metadata or {}).encode()

        # Encrypt with XChaCha20-Poly1305
        ciphertext = crypto_aead_xchacha20poly1305_ietf_encrypt(
            plaintext, additional_data, nonce, self.tx_key
        )

        # Construct message
        header = b"AICO-MSG"
        return header + nonce + len(additional_data).to_bytes(4, 'big') + additional_data + ciphertext

    def decrypt_message(self, encrypted_msg: bytes) -> tuple[bytes, dict]:
        # Parse message format
        header = encrypted_msg[:8]
        nonce = encrypted_msg[8:32]
        metadata_len = int.from_bytes(encrypted_msg[32:36], 'big')
        metadata = json.loads(encrypted_msg[36:36+metadata_len])
        ciphertext = encrypted_msg[36+metadata_len:]

        # Decrypt and authenticate
        plaintext = crypto_aead_xchacha20poly1305_ietf_decrypt(
            ciphertext, json.dumps(metadata).encode(), nonce, self.rx_key
        )

        return plaintext, metadata

Integration with AICO Systems

Key Manager Integration

Transport security integrates with AICO's existing key management:

class TransportKeyManager:
    def __init__(self, aico_key_manager: AICOKeyManager):
        self.key_manager = aico_key_manager

    def derive_transport_keys(self, purpose: str) -> tuple[bytes, bytes]:
        """Derive transport keypair from master key"""
        master_key = self.key_manager.get_master_key()

        # Use AICO's Argon2id derivation for transport keys
        seed = self.key_manager.derive_key(
            master_key, f"transport-{purpose}", 32
        )

        return crypto_sign_seed_keypair(seed)

Message Bus Security

ZeroMQ message bus uses libsodium for internal security:

class SecureMessageBus:
    def __init__(self, identity: ComponentIdentity):
        self.identity = identity
        self.channels = {}  # peer_id -> SecureChannel

    def publish_secure(self, topic: str, message: bytes, target_peers: list = None):
        """Publish encrypted message to specific peers or broadcast"""

        if target_peers:
            # Encrypt for specific peers
            for peer_id in target_peers:
                if peer_id in self.channels:
                    encrypted = self.channels[peer_id].encrypt_message(
                        message, {"topic": topic, "timestamp": time.time()}
                    )
                    self.zmq_socket.send_multipart([f"{topic}.{peer_id}".encode(), encrypted])
        else:
            # Broadcast (requires group key or per-peer encryption)
            self._broadcast_encrypted(topic, message)

API Gateway Security

REST and WebSocket endpoints use the same transport security:

class SecureAPIGateway:
    def __init__(self, backend_identity: ComponentIdentity):
        self.identity = backend_identity
        self.client_channels = {}  # client_id -> SecureChannel

    async def websocket_handler(self, websocket, path):
        # Perform handshake
        client_identity = await self.perform_handshake(websocket)

        # Establish secure channel
        channel = SecureChannel.from_handshake(self.identity, client_identity)
        self.client_channels[client_identity.component_id] = channel

        # Handle encrypted messages
        async for encrypted_msg in websocket:
            plaintext, metadata = channel.decrypt_message(encrypted_msg)
            response = await self.process_api_request(plaintext, metadata)
            encrypted_response = channel.encrypt_message(response)
            await websocket.send(encrypted_response)

Deployment Patterns

Coupled Mode (Same Device)

Components communicate via local sockets with libsodium encryption:

# Local secure communication
class LocalSecureTransport:
    def __init__(self, socket_path: str, identity: ComponentIdentity):
        self.socket_path = socket_path
        self.identity = identity
        self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

    def connect_secure(self):
        self.socket.connect(self.socket_path)
        # Perform libsodium handshake over Unix socket
        peer_identity = self.perform_handshake()
        self.channel = SecureChannel.establish(self.identity, peer_identity)

Detached Mode (Separate Devices)

Components communicate over network with additional security layers:

class NetworkSecureTransport:
    def __init__(self, host: str, port: int, identity: ComponentIdentity):
        self.host = host
        self.port = port
        self.identity = identity

    def connect_secure(self):
        # Establish TCP connection
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect((self.host, self.port))

        # Perform libsodium handshake over network
        peer_identity = self.perform_network_handshake()
        self.channel = SecureChannel.establish(self.identity, peer_identity)

        # Additional network security measures
        self.enable_keepalive()
        self.setup_connection_monitoring()

Security Properties

Cryptographic Guarantees

Confidentiality: XChaCha20 encryption with 256-bit keys Authenticity: Poly1305 authentication prevents tampering Forward Secrecy: Session keys rotated, old messages remain secure Replay Protection: Nonce-based message ordering prevents replay attacks

Threat Model Coverage

Threat Mitigation
Eavesdropping XChaCha20 encryption
Message Tampering Poly1305 authentication
Identity Spoofing Ed25519 component signatures
Replay Attacks Nonce validation and timestamps
Man-in-the-Middle Mutual authentication with persistent identities
Key Compromise Session key rotation and forward secrecy

Performance Characteristics

Handshake Overhead: ~2ms for key exchange and authentication Message Overhead: 48 bytes per message (header + nonce + tag) Throughput Impact: <5% performance reduction vs plaintext CPU Usage: Hardware-accelerated ChaCha20 on modern processors

Configuration

Security Parameters

Transport security is configured via security.yaml:

security:
  transport:
    # Cryptographic algorithms
    encryption: "xchacha20poly1305"
    key_exchange: "x25519"
    authentication: "ed25519"

    # Session management
    session_timeout: 3600  # 1 hour
    key_rotation_interval: 86400  # 24 hours
    max_message_size: 16777216  # 16MB

    # Network settings
    handshake_timeout: 30
    keepalive_interval: 60
    connection_retry_limit: 3

    # Security policies
    require_fresh_handshake: true
    allow_key_reuse: false
    enforce_message_ordering: true

Component Configuration

Each component configures transport security:

# Backend configuration
transport_config = {
    "identity_file": "~/.aico/backend_identity.key",
    "listen_address": "127.0.0.1:8771",
    "allowed_components": ["frontend", "cli", "studio"],
    "security_level": "high"
}

# Frontend configuration  
transport_config = {
    "identity_file": "~/.aico/frontend_identity.key", 
    "backend_address": "127.0.0.1:8771",
    "auto_reconnect": true,
    "security_level": "high"
}

Security Considerations

Key Management

Identity Persistence: Component identities stored in AICO keyring Key Rotation: Automatic session key rotation every 24 hours Compromise Recovery: Identity revocation and regeneration procedures

Network Security

Connection Validation: Peer identity verification before key exchange Traffic Analysis: Metadata protection through consistent message sizes Denial of Service: Rate limiting and connection throttling

Implementation Security

Memory Safety: Secure key erasure after use Side Channel Protection: Constant-time operations where possible Error Handling: Secure failure modes without information leakage


This transport security architecture provides AICO with robust, application-level encryption that integrates seamlessly with existing systems while maintaining performance and usability across all deployment scenarios.