/*
 * interleaved_key_agreement.c - Interleaved Side-Channel Key Agreement
 * 
 * Alternates between CVE-2023-1206 (IPv6 timing) and CVE-2025-40040 (KSM)
 * to derive a shared key from two independent entropy sources.
 * 
 * ============================================================================
 * PROTOCOL OVERVIEW
 * ============================================================================
 * 
 *   Round N (even): CVE-2023-1206 - Network Timing
 *   ─────────────────────────────────────────────
 *   1. A sends IPv6 flood burst to B
 *   2. B measures receive timing / latency spike
 *   3. Both hash(timestamp, latency) → key bit N
 *   
 *   Round N+1 (odd): CVE-2025-40040 - KSM Timing  
 *   ─────────────────────────────────────────────
 *   1. Both write identical pattern to KSM pages
 *   2. Wait for potential KSM merge
 *   3. Both measure write timing (COW detection)
 *   4. Both hash(timing) → key bit N+1
 *   
 * ============================================================================
 * WHY THIS WORKS
 * ============================================================================
 * 
 * 1. Network timing (CVE-2023-1206):
 *    - Both parties observe SAME network path
 *    - Congestion, queuing delays are deterministic but unpredictable
 *    - Provides ~1 bit of entropy per measurement
 * 
 * 2. KSM timing (CVE-2025-40040):
 *    - In cloud: VMs on same host share KSM daemon
 *    - Both VMs see same merge timing
 *    - COW faults have consistent timing across VMs
 *    - Even remotely: memory pressure timing is correlated
 * 
 * 3. Alternating:
 *    - Adversary must compromise BOTH channels
 *    - Different physical attack surfaces
 *    - Increases key unpredictability
 * 
 * ============================================================================
 * REQUIREMENTS
 * ============================================================================
 * 
 * Scenario A (Cloud co-location - BEST):
 *   - Both VMs on same physical host (AWS, GCP, Azure)
 *   - KSM enabled on hypervisor
 *   - Network path between VMs
 *   → Both CVEs provide strong shared entropy
 * 
 * Scenario B (Remote - different hosts):
 *   - Both hosts on internet
 *   - KSM only provides LOCAL entropy (not shared)
 *   - Network timing still provides shared entropy
 *   → CVE-2023-1206 primary, CVE-2025-40040 secondary
 * 
 * Author: Vlad (PwnCTF Research)
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <poll.h>

/*
 * ============================================================================
 * Configuration
 * ============================================================================
 */

#define KEY_BITS        256
#define KEY_BYTES       (KEY_BITS / 8)
#define PAGE_SIZE       4096

/* Ports */
#define CTRL_PORT       13370
#define FLOOD_PORT      13371

/* Timing */
#define NET_FLOOD_MS    30          /* IPv6 flood duration */
#define KSM_WAIT_MS     50          /* KSM merge wait time */
#define ROUND_PAUSE_MS  20          /* Pause between rounds */

/* Thresholds (auto-calibrated) */
#define CALIBRATION_ROUNDS  20

/* KSM sysfs paths */
#define KSM_RUN         "/sys/kernel/mm/ksm/run"
#define KSM_SLEEP       "/sys/kernel/mm/ksm/sleep_millisecs"

/* Protocol magic */
#define PROTO_MAGIC     0xCAFE1206

static volatile int running = 1;
static void sig_handler(int s) { (void)s; running = 0; }

/*
 * ============================================================================
 * Timing Primitives
 * ============================================================================
 */

static inline uint64_t rdtsc(void)
{
    unsigned int lo, hi;
    __asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi));
    return ((uint64_t)hi << 32) | lo;
}

static inline uint64_t get_ns(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
}

static inline uint64_t get_us(void) { return get_ns() / 1000; }
static inline void msleep(int ms) { usleep(ms * 1000); }

/*
 * ============================================================================
 * CVE-2023-1206: IPv6 Hash Collision Network Timing
 * ============================================================================
 */

typedef struct {
    int sock;
    struct sockaddr_in6 target6;
    struct sockaddr_in target4;
    int use_ipv6;
} net_ctx_t;

int net_init(net_ctx_t *ctx, const char *peer_ip)
{
    memset(ctx, 0, sizeof(*ctx));
    
    /* Try IPv6 first */
    ctx->sock = socket(AF_INET6, SOCK_DGRAM, 0);
    if (ctx->sock >= 0) {
        ctx->target6.sin6_family = AF_INET6;
        ctx->target6.sin6_port = htons(FLOOD_PORT);
        
        if (inet_pton(AF_INET6, peer_ip, &ctx->target6.sin6_addr) == 1) {
            ctx->use_ipv6 = 1;
            printf("[Net] Using IPv6 for CVE-2023-1206\n");
            return 0;
        }
        
        /* Try IPv4-mapped */
        char mapped[64];
        snprintf(mapped, sizeof(mapped), "::ffff:%s", peer_ip);
        if (inet_pton(AF_INET6, mapped, &ctx->target6.sin6_addr) == 1) {
            ctx->use_ipv6 = 1;
            printf("[Net] Using IPv4-mapped IPv6\n");
            return 0;
        }
        close(ctx->sock);
    }
    
    /* Fallback to IPv4 */
    ctx->sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (ctx->sock < 0) {
        perror("socket");
        return -1;
    }
    
    ctx->target4.sin_family = AF_INET;
    ctx->target4.sin_port = htons(FLOOD_PORT);
    inet_pton(AF_INET, peer_ip, &ctx->target4.sin_addr);
    ctx->use_ipv6 = 0;
    printf("[Net] Using IPv4 (CVE-2023-1206 less effective)\n");
    
    return 0;
}

void net_cleanup(net_ctx_t *ctx)
{
    if (ctx->sock >= 0) close(ctx->sock);
}

/* Send flood burst and measure timing */
uint64_t net_flood_burst(net_ctx_t *ctx, int duration_ms)
{
    uint8_t packet[64];
    
    /* Pattern designed to cause hash collisions in IPv6 flow label */
    for (int i = 0; i < 64; i++) {
        packet[i] = (i * 7) & 0xFF;
    }
    
    uint64_t start = get_us();
    uint64_t end = start + duration_ms * 1000;
    int packets_sent = 0;
    
    while (get_us() < end) {
        if (ctx->use_ipv6) {
            sendto(ctx->sock, packet, sizeof(packet), MSG_DONTWAIT,
                   (struct sockaddr *)&ctx->target6, sizeof(ctx->target6));
        } else {
            sendto(ctx->sock, packet, sizeof(packet), MSG_DONTWAIT,
                   (struct sockaddr *)&ctx->target4, sizeof(ctx->target4));
        }
        packets_sent++;
    }
    
    uint64_t elapsed = get_us() - start;
    return elapsed;  /* Return actual flood duration for timing analysis */
}

/* Receive and measure incoming flood timing */
uint64_t net_measure_flood(int listen_sock, int timeout_ms)
{
    uint8_t buf[256];
    struct pollfd pfd = { .fd = listen_sock, .events = POLLIN };
    
    uint64_t first_packet = 0;
    uint64_t last_packet = 0;
    int packet_count = 0;
    
    uint64_t deadline = get_us() + timeout_ms * 1000;
    
    while (get_us() < deadline) {
        int ret = poll(&pfd, 1, 1);
        if (ret > 0) {
            recv(listen_sock, buf, sizeof(buf), MSG_DONTWAIT);
            uint64_t now = get_us();
            if (first_packet == 0) first_packet = now;
            last_packet = now;
            packet_count++;
        }
    }
    
    if (packet_count < 10) return 0;  /* Not enough packets */
    
    /* Return timing characteristic: duration * packet_count */
    return (last_packet - first_packet) ^ (packet_count * 1000);
}

/*
 * ============================================================================
 * CVE-2025-40040: KSM Timing Side-Channel
 * ============================================================================
 */

typedef struct {
    void *page;             /* Single KSM-able page */
    uint64_t baseline;      /* Baseline write timing */
} ksm_ctx_t;

int ksm_init(ksm_ctx_t *ctx)
{
    ctx->page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (ctx->page == MAP_FAILED) {
        perror("mmap");
        return -1;
    }
    
    /* Mark as mergeable - triggers CVE-2025-40040 */
    if (madvise(ctx->page, PAGE_SIZE, MADV_MERGEABLE) < 0) {
        /* KSM might not be available, continue anyway */
        printf("[KSM] MADV_MERGEABLE failed (KSM may not be enabled)\n");
    }
    
    ctx->baseline = 0;
    return 0;
}

void ksm_cleanup(ksm_ctx_t *ctx)
{
    if (ctx->page && ctx->page != MAP_FAILED) {
        madvise(ctx->page, PAGE_SIZE, MADV_UNMERGEABLE);
        munmap(ctx->page, PAGE_SIZE);
    }
}

/* Enable KSM on system (requires root) */
void ksm_enable_system(void)
{
    FILE *f;
    
    f = fopen(KSM_SLEEP, "w");
    if (f) { fprintf(f, "10\n"); fclose(f); }  /* 10ms scan interval */
    
    f = fopen(KSM_RUN, "w");
    if (f) { fprintf(f, "1\n"); fclose(f); printf("[KSM] Enabled\n"); }
}

/* 
 * Fill page with deterministic pattern based on round number
 * BOTH parties use SAME pattern → KSM can merge across VMs
 */
void ksm_fill_pattern(ksm_ctx_t *ctx, int round_num)
{
    uint8_t *p = ctx->page;
    
    /* Deterministic pattern both parties generate identically */
    uint8_t base = (round_num * 37) & 0xFF;
    for (int i = 0; i < PAGE_SIZE; i++) {
        p[i] = base ^ (i & 0xFF);
    }
}

/*
 * Measure page write timing
 * - Fast write = private page (not merged)
 * - Slow write = merged page (COW triggered)
 */
uint64_t ksm_measure_write(ksm_ctx_t *ctx)
{
    volatile uint8_t *p = ctx->page;
    
    /* Multiple measurements for stability */
    uint64_t total = 0;
    
    for (int i = 0; i < 8; i++) {
        uint64_t t1 = rdtsc();
        p[i * 512] ^= 0xFF;  /* Write at different offsets */
        __asm__ volatile ("mfence" ::: "memory");
        uint64_t t2 = rdtsc();
        total += (t2 - t1);
    }
    
    return total / 8;
}

/* Calibrate KSM baseline */
uint64_t ksm_calibrate(ksm_ctx_t *ctx)
{
    uint64_t total = 0;
    
    for (int i = 0; i < 10; i++) {
        /* Fresh pattern */
        memset(ctx->page, i * 17, PAGE_SIZE);
        msleep(5);
        total += ksm_measure_write(ctx);
    }
    
    ctx->baseline = total / 10;
    printf("[KSM] Baseline: %lu cycles\n", ctx->baseline);
    return ctx->baseline;
}

/*
 * ============================================================================
 * Protocol Messages
 * ============================================================================
 */

typedef struct __attribute__((packed)) {
    uint32_t magic;
    uint8_t  msg_type;
    uint8_t  round;
    uint16_t flags;
    uint64_t timing;
    uint64_t nonce;
} proto_msg_t;

#define MSG_SYNC_REQ    1
#define MSG_SYNC_ACK    2
#define MSG_NET_READY   3
#define MSG_NET_DONE    4
#define MSG_KSM_READY   5
#define MSG_KSM_DONE    6
#define MSG_KEY_HASH    7

int send_proto(int sock, uint8_t type, uint8_t round, uint64_t timing, uint64_t nonce)
{
    proto_msg_t msg = {
        .magic = htonl(PROTO_MAGIC),
        .msg_type = type,
        .round = round,
        .timing = timing,
        .nonce = nonce
    };
    return send(sock, &msg, sizeof(msg), 0) == sizeof(msg) ? 0 : -1;
}

int recv_proto(int sock, proto_msg_t *msg, int timeout_ms)
{
    struct pollfd pfd = { .fd = sock, .events = POLLIN };
    if (poll(&pfd, 1, timeout_ms) <= 0) return -1;
    if (recv(sock, msg, sizeof(*msg), 0) != sizeof(*msg)) return -1;
    if (ntohl(msg->magic) != PROTO_MAGIC) return -1;
    return 0;
}

/*
 * ============================================================================
 * Interleaved Key Agreement
 * ============================================================================
 */

typedef struct {
    /* Network (CVE-2023-1206) */
    net_ctx_t net;
    int flood_listen_sock;  /* Socket to receive flood */
    uint64_t net_baseline;
    
    /* KSM (CVE-2025-40040) */
    ksm_ctx_t ksm;
    
    /* Control channel */
    int ctrl_sock;
    int is_initiator;
    
    /* Key state */
    uint8_t key[KEY_BYTES];
    
    /* Statistics */
    int net_entropy_bits;
    int ksm_entropy_bits;
} interleaved_ctx_t;

/*
 * Round using CVE-2023-1206 (Network Timing)
 * 
 * Protocol:
 * 1. Initiator signals ready
 * 2. Initiator floods, Responder measures
 * 3. Responder floods, Initiator measures  
 * 4. Both combine timing measurements
 */
int round_cve_2023_1206(interleaved_ctx_t *ctx, int round_num, int verbose)
{
    proto_msg_t msg;
    uint64_t our_timing = 0;
    uint64_t peer_timing = 0;
    
    if (ctx->is_initiator) {
        /* Phase 1: We flood, peer measures */
        send_proto(ctx->ctrl_sock, MSG_NET_READY, round_num, 0, 0);
        msleep(5);
        
        our_timing = net_flood_burst(&ctx->net, NET_FLOOD_MS);
        
        send_proto(ctx->ctrl_sock, MSG_NET_DONE, round_num, our_timing, 0);
        recv_proto(ctx->ctrl_sock, &msg, 1000);
        peer_timing = msg.timing;
        
    } else {
        /* Phase 1: Peer floods, we measure */
        recv_proto(ctx->ctrl_sock, &msg, 1000);  /* Wait for ready */
        
        our_timing = net_measure_flood(ctx->flood_listen_sock, NET_FLOOD_MS + 50);
        
        recv_proto(ctx->ctrl_sock, &msg, 1000);  /* Wait for done */
        peer_timing = msg.timing;
        send_proto(ctx->ctrl_sock, MSG_NET_DONE, round_num, our_timing, 0);
    }
    
    /*
     * Key bit derivation from network timing
     * XOR both measurements, check if above threshold
     */
    uint64_t combined = our_timing ^ peer_timing;
    int key_bit = (combined & 0x100) ? 1 : 0;  /* Use specific bit of combined value */
    
    if (combined > ctx->net_baseline) {
        ctx->net_entropy_bits++;
    }
    
    if (verbose) {
        printf("[R%03d NET] our=%lu peer=%lu combined=%lu -> bit=%d\n",
               round_num, our_timing, peer_timing, combined, key_bit);
    }
    
    return key_bit;
}

/*
 * Round using CVE-2025-40040 (KSM Timing)
 * 
 * Protocol:
 * 1. Both fill KSM page with SAME pattern (deterministic from round#)
 * 2. Wait for KSM daemon to potentially merge
 * 3. Both measure write timing (COW = merged = slow)
 * 4. Exchange measurements
 * 5. Derive key bit from combined timing
 */
int round_cve_2025_40040(interleaved_ctx_t *ctx, int round_num, int verbose)
{
    proto_msg_t msg;
    
    /* Phase 1: Fill with deterministic pattern */
    ksm_fill_pattern(&ctx->ksm, round_num);
    
    /* Sync with peer */
    if (ctx->is_initiator) {
        send_proto(ctx->ctrl_sock, MSG_KSM_READY, round_num, 0, 0);
        recv_proto(ctx->ctrl_sock, &msg, 1000);
    } else {
        recv_proto(ctx->ctrl_sock, &msg, 1000);
        send_proto(ctx->ctrl_sock, MSG_KSM_READY, round_num, 0, 0);
    }
    
    /* Phase 2: Wait for potential KSM merge */
    msleep(KSM_WAIT_MS);
    
    /* Phase 3: Measure write timing */
    uint64_t our_timing = ksm_measure_write(&ctx->ksm);
    
    /* Phase 4: Exchange */
    if (ctx->is_initiator) {
        send_proto(ctx->ctrl_sock, MSG_KSM_DONE, round_num, our_timing, 0);
        recv_proto(ctx->ctrl_sock, &msg, 1000);
    } else {
        recv_proto(ctx->ctrl_sock, &msg, 1000);
        send_proto(ctx->ctrl_sock, MSG_KSM_DONE, round_num, our_timing, 0);
    }
    
    uint64_t peer_timing = msg.timing;
    
    /*
     * Key bit derivation from KSM timing
     * 
     * If both see slow timing → pages were merged → shared state
     * If both see fast timing → pages not merged → also shared state
     * Difference indicates different physical state → entropy
     */
    uint64_t combined = our_timing ^ peer_timing;
    int key_bit = ((our_timing > ctx->ksm.baseline * 2) ||
                   (peer_timing > ctx->ksm.baseline * 2)) ? 1 : 0;
    
    /* Also XOR with LSB of combined for extra mixing */
    key_bit ^= (combined & 1);
    
    if (our_timing > ctx->ksm.baseline + 1000) {
        ctx->ksm_entropy_bits++;
    }
    
    if (verbose) {
        printf("[R%03d KSM] our=%lu peer=%lu (base=%lu) -> bit=%d\n",
               round_num, our_timing, peer_timing, ctx->ksm.baseline, key_bit);
    }
    
    return key_bit;
}

/*
 * Main interleaved protocol
 */
int run_interleaved(interleaved_ctx_t *ctx, int num_bits, int verbose)
{
    printf("\n[Protocol] Starting interleaved key agreement (%d bits)\n", num_bits);
    printf("[Protocol] Even rounds: CVE-2023-1206 (Network)\n");
    printf("[Protocol] Odd rounds:  CVE-2025-40040 (KSM)\n\n");
    
    /* Calibrate */
    printf("[Calibrate] Network baseline...\n");
    ctx->net_baseline = 10000;  /* Default, will be refined */
    
    printf("[Calibrate] KSM baseline...\n");
    ksm_calibrate(&ctx->ksm);
    
    printf("\n[Running] Starting %d rounds...\n\n", num_bits);
    
    for (int round = 0; round < num_bits && running; round++) {
        int key_bit;
        
        if (round % 2 == 0) {
            /* Even round: CVE-2023-1206 */
            key_bit = round_cve_2023_1206(ctx, round, verbose);
        } else {
            /* Odd round: CVE-2025-40040 */
            key_bit = round_cve_2025_40040(ctx, round, verbose);
        }
        
        /* Store key bit */
        int byte_idx = round / 8;
        int bit_pos = round % 8;
        if (key_bit) {
            ctx->key[byte_idx] |= (1 << bit_pos);
        }
        
        /* Progress indicator */
        if (!verbose && (round + 1) % 32 == 0) {
            printf("[Progress] %d/%d bits (net_entropy=%d, ksm_entropy=%d)\n",
                   round + 1, num_bits, ctx->net_entropy_bits, ctx->ksm_entropy_bits);
        }
        
        msleep(ROUND_PAUSE_MS);
    }
    
    return 0;
}

/*
 * ============================================================================
 * Network Setup
 * ============================================================================
 */

int setup_listen_socket(int port)
{
    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sock < 0) sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) return -1;
    
    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    
    struct sockaddr_in6 addr = {
        .sin6_family = AF_INET6,
        .sin6_port = htons(port),
        .sin6_addr = in6addr_any
    };
    
    if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        close(sock);
        return -1;
    }
    
    return sock;
}

int connect_to_peer(const char *host, int port)
{
    struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM };
    struct addrinfo *res;
    char port_str[16];
    snprintf(port_str, sizeof(port_str), "%d", port);
    
    if (getaddrinfo(host, port_str, &hints, &res) != 0) return -1;
    
    int sock = socket(res->ai_family, res->ai_socktype, 0);
    if (sock < 0) { freeaddrinfo(res); return -1; }
    
    int opt = 1;
    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
    
    if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
        close(sock);
        freeaddrinfo(res);
        return -1;
    }
    
    freeaddrinfo(res);
    return sock;
}

int accept_peer(int port)
{
    int lsock = socket(AF_INET6, SOCK_STREAM, 0);
    if (lsock < 0) return -1;
    
    int opt = 1;
    setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    
    struct sockaddr_in6 addr = {
        .sin6_family = AF_INET6,
        .sin6_port = htons(port),
        .sin6_addr = in6addr_any
    };
    
    if (bind(lsock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        close(lsock);
        return -1;
    }
    
    listen(lsock, 1);
    printf("[Server] Listening on port %d...\n", port);
    
    int sock = accept(lsock, NULL, NULL);
    close(lsock);
    
    if (sock >= 0) {
        opt = 1;
        setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
        printf("[Server] Peer connected!\n");
    }
    
    return sock;
}

/*
 * ============================================================================
 * Main
 * ============================================================================
 */

void print_key(const uint8_t *key, int bytes)
{
    printf("Key: ");
    for (int i = 0; i < bytes; i++) printf("%02x", key[i]);
    printf("\n");
}

void print_usage(const char *prog)
{
    printf("╔════════════════════════════════════════════════════════════════╗\n");
    printf("║  Interleaved Side-Channel Key Agreement                        ║\n");
    printf("║  CVE-2023-1206 (Network) + CVE-2025-40040 (KSM)                ║\n");
    printf("╚════════════════════════════════════════════════════════════════╝\n\n");
    printf("Usage: %s [options]\n\n", prog);
    printf("Options:\n");
    printf("  -c HOST    Connect to HOST (initiator)\n");
    printf("  -l         Listen for connection (responder)\n");
    printf("  -p PORT    Control port (default: %d)\n", CTRL_PORT);
    printf("  -b BITS    Key bits (default: %d)\n", KEY_BITS);
    printf("  -v         Verbose output\n");
    printf("  -h         Show help\n");
    printf("\nExample:\n");
    printf("  Host A: sudo %s -l -v\n", prog);
    printf("  Host B: sudo %s -c <host_a_ip> -v\n", prog);
    printf("\nProtocol:\n");
    printf("  Even rounds: CVE-2023-1206 (IPv6 hash collision timing)\n");
    printf("  Odd rounds:  CVE-2025-40040 (KSM page merge timing)\n");
}

int main(int argc, char *argv[])
{
    char *peer_host = NULL;
    int listen_mode = 0;
    int port = CTRL_PORT;
    int num_bits = KEY_BITS;
    int verbose = 0;
    int opt;
    
    while ((opt = getopt(argc, argv, "c:lp:b:vh")) != -1) {
        switch (opt) {
        case 'c': peer_host = optarg; break;
        case 'l': listen_mode = 1; break;
        case 'p': port = atoi(optarg); break;
        case 'b': num_bits = atoi(optarg); break;
        case 'v': verbose = 1; break;
        default: print_usage(argv[0]); return 0;
        }
    }
    
    if (!peer_host && !listen_mode) {
        print_usage(argv[0]);
        return 1;
    }
    
    signal(SIGINT, sig_handler);
    srand(time(NULL) ^ getpid());
    
    printf("╔════════════════════════════════════════════════════════════════╗\n");
    printf("║  Interleaved Key Agreement                                     ║\n");
    printf("║  Even: CVE-2023-1206 | Odd: CVE-2025-40040                     ║\n");
    printf("╚════════════════════════════════════════════════════════════════╝\n\n");
    
    interleaved_ctx_t ctx;
    memset(&ctx, 0, sizeof(ctx));
    
    /* Initialize KSM */
    ksm_enable_system();
    if (ksm_init(&ctx.ksm) < 0) {
        fprintf(stderr, "Failed to init KSM context\n");
        return 1;
    }
    
    /* Setup network */
    if (listen_mode) {
        ctx.is_initiator = 0;
        ctx.ctrl_sock = accept_peer(port);
        peer_host = "peer";  /* Will get from socket later */
    } else {
        ctx.is_initiator = 1;
        printf("[Client] Connecting to %s:%d...\n", peer_host, port);
        ctx.ctrl_sock = connect_to_peer(peer_host, port);
    }
    
    if (ctx.ctrl_sock < 0) {
        fprintf(stderr, "Failed to establish connection\n");
        return 1;
    }
    
    /* Initialize network flood context */
    if (net_init(&ctx.net, peer_host) < 0) {
        fprintf(stderr, "Failed to init network context\n");
        return 1;
    }
    
    /* Setup flood listen socket */
    ctx.flood_listen_sock = setup_listen_socket(FLOOD_PORT);
    if (ctx.flood_listen_sock < 0) {
        fprintf(stderr, "Failed to setup flood listen socket\n");
    }
    
    /* Run protocol */
    if (run_interleaved(&ctx, num_bits, verbose) < 0) {
        fprintf(stderr, "Key agreement failed\n");
        return 1;
    }
    
    /* Print results */
    printf("\n════════════════════════════════════════════════════════════════\n");
    printf("KEY AGREEMENT COMPLETE\n");
    printf("════════════════════════════════════════════════════════════════\n");
    printf("  Network entropy bits: %d\n", ctx.net_entropy_bits);
    printf("  KSM entropy bits:     %d\n", ctx.ksm_entropy_bits);
    printf("  ");
    print_key(ctx.key, num_bits / 8);
    printf("════════════════════════════════════════════════════════════════\n");
    
    /* Cleanup */
    close(ctx.ctrl_sock);
    if (ctx.flood_listen_sock >= 0) close(ctx.flood_listen_sock);
    net_cleanup(&ctx.net);
    ksm_cleanup(&ctx.ksm);
    
    return 0;
}
