Assignment: Building a Small Peer-to-Peer (P2P) Network in C with TLS 1.3

Overview

In this assignment, you will create a simple peer-to-peer (P2P) network consisting of four clients, implemented in C, with TLS 1.3 securing all communications. Each peer should be able to connect to and communicate with the others without relying on a centralized server, ensuring all data transferred is encrypted via TLS 1.3.


Learning Objectives

  1. Network Programming in C: Gain practical experience with low-level socket programming and concurrency.
  2. TLS 1.3 Integration: Learn how to establish secure connections using OpenSSL (or another TLS library) in C.
  3. P2P Architecture: Understand how peers discover one another, exchange messages, and maintain a decentralized network.

Requirements

  1. Number of Peers
    • Implement four distinct clients (peers). They can run on the same machine (using different ports) or on different machines/VMs.
  2. Peer Discovery
    • Provide a simple mechanism for peers to discover or connect to each other:
      • Option A: Hardcode a list of IPs/ports.
      • Option B: Have one node function as a known bootstrap peer that new peers contact first.
    • Each peer should maintain a list of currently known peers.
  3. Encrypted Communication via TLS 1.3
    • All data exchanges between peers must occur over a TLS 1.3 connection:
      • Set up a TLS server context on each peer for inbound connections.
      • Set up a TLS client context on each peer when connecting to another peer.
    • Use OpenSSL (or a comparable library) to implement TLS 1.3.
    • Each peer should have its own self-signed certificate and private key, or you may use a single certificate authority (CA) to sign the certificates if you want to simulate a more realistic setup.
  4. Messaging Protocol
    • Define and implement a small protocol, with at least these message types:
      • JOIN: New client introduction.
      • BROADCAST/CHAT: Sending a message to all connected peers.
      • (Optional) LEAVE: Graceful exit from the network.
    • Choose a format (e.g., line-based text, JSON-like strings, structs) for these messages.
  5. Core Functionality
    • Each peer should be able to:
      1. Connect to the network (via TLS handshake) using the discovery mechanism.
      2. Send messages to other peers over the secure channel.
      3. Receive messages from other peers over the secure channel.
      4. Broadcast a message to all peers.
  6. Logging/Output
    • Each peer should print/log key events (e.g., “Incoming connection from Peer X,” “Sending broadcast to 3 peers,” “Received message: Hello!”).

Suggested Steps

  1. Set Up TLS Certificates
    • Use OpenSSL commands to generate a certificate and private key for each peer (or use a single CA to sign them).bashCopy# Example self-signed certificate generation openssl req -x509 -newkey rsa:2048 -nodes -keyout peer1.key -out peer1.crt -days 365
    • In your code, load these certificates in both server and client contexts.
  2. Build a Single Peer with TLS
    • Create a listening socket (server mode) wrapped in a TLS server context.
    • Create a client mode TLS context for outbound connections to other peers.
    • Test sending/receiving a simple encrypted message with a single peer pair.
  3. Expand to Four Peers
    • Add the discovery mechanism so that a new peer can connect to the existing network.
    • Each new peer obtains (and stores) the addresses/ports (and possibly certificate info) of other peers.
  4. Implement Broadcasting
    • Ensure a message from one peer is distributed to all others (direct or indirect).
    • Print out messages upon receipt.
  5. Demonstrate End-to-End Encrypted Communication
    • Show that each peer can securely connect to any other, exchange messages, and broadcast data.
  6. Security Considerations (Optional/Advanced)
    • Validate certificates (e.g., using a CA-signed approach) or accept self-signed certs with known fingerprints.
    • Consider certificate revocation, cipher suite selection, or advanced TLS features if desired.

Deliverables

  1. Source Code
    • All C source (.c.h) files implementing the P2P logic and TLS 1.3 integration.
    • Makefile (or build script) so your code can be compiled on a standard Linux environment.
    • Any scripts or instructions used to generate/test certificates.
  2. Demo or Screenshots
    • Show four peers running, connecting to each other, and exchanging encrypted messages.
    • Provide console output logs that confirm TLS handshakes are successful and messages are received.
  3. Short Write-Up / README
    • Explain how to run your peers, the protocol (JOIN, BROADCAST, etc.), and how you integrated TLS 1.3.
    • If you encountered any challenges (certificate handling, concurrency, etc.), describe how you solved them.

Grading Criteria (Example)

CriterionDescriptionPoints
P2P ArchitectureCorrectly implements peer discovery and communication20
TLS 1.3 EncryptionSuccessful secure channels, proper certificate usage, encryption30
Messaging ProtocolWell-defined, consistent, and documented message structure20
Functionality & ReliabilityAll operations (join, broadcast, receive messages) work properly20
Documentation & ClarityCode readability, instructions, short write-up, logs/screenshots10
Total100

Example: Simple TLS 1.3 Connection in C with OpenSSL

Below is a minimal illustration of using OpenSSL to set up a TLS 1.3 server and client. The example omits many details (error handling, concurrency, sending actual messages, etc.). For production code, be sure to add proper checks, error handling, and multi-threading or select()/poll() logic to handle multiple clients.

Note: This example assumes you have OpenSSL 1.1.1 or newer (which supports TLS 1.3) and that you have already generated a certificate (server.crt) and private key (server.key) for testing.

Common Initialization

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define PORT 5001

void init_openssl() {
    SSL_library_init();
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}

void cleanup_openssl() {
    EVP_cleanup();
}

SSL_CTX* create_context_server() {
    const SSL_METHOD *method = TLS_server_method();
    SSL_CTX *ctx = SSL_CTX_new(method);
    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
    // Restrict to TLS 1.3 only
    SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
    SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
    return ctx;
}

SSL_CTX* create_context_client() {
    const SSL_METHOD *method = TLS_client_method();
    SSL_CTX *ctx = SSL_CTX_new(method);
    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
    // Restrict to TLS 1.3 only
    SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
    SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
    return ctx;
}

void configure_context(SSL_CTX *ctx, const char *cert_file, const char *key_file) {
    // For server mode, we typically load cert and key:
    if (SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
    if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0 ) {
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
    // Optionally require client certificate verification:
    // SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
    // SSL_CTX_load_verify_locations(ctx, "ca.crt", NULL);
}

Example: Server-Side TLS

void run_tls_server(const char *cert_file, const char *key_file) {
    SSL_CTX *ctx = create_context_server();
    configure_context(ctx, cert_file, key_file);

    int server_fd;
    struct sockaddr_in addr;
    int opt = 1;
    int addrlen = sizeof(addr);

    // Create TCP socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);

    // Bind socket to specified port
    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // Listen for incoming connections
    if (listen(server_fd, 1) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port %d...\n", PORT);

    while (1) {
        struct sockaddr_in client_addr;
        socklen_t client_len = sizeof(client_addr);
        int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
        if (client_fd < 0) {
            perror("accept");
            continue;
        }

        SSL *ssl = SSL_new(ctx);
        SSL_set_fd(ssl, client_fd);

        if (SSL_accept(ssl) <= 0) {
            ERR_print_errors_fp(stderr);
        } else {
            printf("TLS 1.3 handshake successful with client.\n");
            // Here you can read/write encrypted data:
            char buf[1024] = {0};
            int bytes = SSL_read(ssl, buf, sizeof(buf));
            if (bytes > 0) {
                printf("Received: %s\n", buf);
                SSL_write(ssl, "Hello, TLS client!", 18);
            }
        }

        SSL_shutdown(ssl);
        SSL_free(ssl);
        close(client_fd);
    }

    close(server_fd);
    SSL_CTX_free(ctx);
}

Example: Client-Side TLS

void run_tls_client(const char *server_ip, const char *cert_file, const char *key_file) {
    SSL_CTX *ctx = create_context_client();
    configure_context(ctx, cert_file, key_file);

    // For client mode, you might also set up CA verification if needed:
    // SSL_CTX_load_verify_locations(ctx, "ca.crt", NULL);
    // SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    int sock;
    struct sockaddr_in server_addr;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket creation error");
        exit(EXIT_FAILURE);
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) {
        perror("Invalid address / Address not supported");
        exit(EXIT_FAILURE);
    }

    if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Connection failed");
        exit(EXIT_FAILURE);
    }

    SSL *ssl = SSL_new(ctx);
    SSL_set_fd(ssl, sock);

    if (SSL_connect(ssl) <= 0) {
        ERR_print_errors_fp(stderr);
    } else {
        printf("TLS 1.3 handshake successful with server.\n");
        SSL_write(ssl, "Hello, TLS server!", 18);

        char buf[1024] = {0};
        int bytes = SSL_read(ssl, buf, sizeof(buf));
        if (bytes > 0) {
            printf("Server replied: %s\n", buf);
        }
    }

    SSL_shutdown(ssl);
    SSL_free(ssl);
    close(sock);
    SSL_CTX_free(ctx);
}

Putting It All Together

  • In your main(), you might parse command-line arguments to decide whether to launch as a server or a client (or both, for a peer).
  • Each peer can run both run_tls_server(...) (on a separate thread or process) and connect to other peers using run_tls_client(...).
  • Extend the code with a real message loop, concurrency, and your P2P protocol logic (JOIN, BROADCAST, etc.).

Final Notes

  1. Certificate Management: Each peer needs its own certificate/private key pair—or a shared CA with unique certs.
  2. Concurrency: For multiple simultaneous connections, consider using threads (pthread_create) or select() for non-blocking I/O.
  3. Error Handling: Always check return values from OpenSSL and socket calls.
  4. Security: In a more advanced setup, verify certificate chains, implement mutual authentication, and consider cipher suite restrictions.

With these steps and the example TLS code in hand, you should be able to build out a four-peer, TLS 1.3–protected network in C. Good luck, and happy coding!