Skip to content

Instantly share code, notes, and snippets.

@richard-ramos
Last active February 4, 2025 22:24
Show Gist options
  • Select an option

  • Save richard-ramos/e01c9ec091a67e2e6042a86bf7359c26 to your computer and use it in GitHub Desktop.

Select an option

Save richard-ramos/e01c9ec091a67e2e6042a86bf7359c26 to your computer and use it in GitHub Desktop.
PicoTLS Handshake
// client.c
#include <picotls.h>
#include "picotls/openssl.h"
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8443
#define BUFFER_SIZE 1024
// In the same dir of picotls, after doing a `cmake .` and a `make`.
// gcc client.c -o client libpicotls-core.a libpicotls-openssl.a -Iinclude -lcrypto
int main(void)
{
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
ptls_context_t ctx = {
.random_bytes = ptls_openssl_random_bytes,
.get_time = &ptls_get_time,
.key_exchanges = ptls_openssl_key_exchanges,
.cipher_suites = ptls_openssl_cipher_suites,
};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("Invalid address");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Connection failed");
return -1;
}
printf("Connected to server\n");
// Create TLS connection
ptls_t *tls = ptls_new(&ctx, 0); // 0 for client
if (tls == NULL) {
fprintf(stderr, "Failed to create TLS context\n");
return -1;
}
ptls_buffer_t sendbuf, recvbuf;
ptls_buffer_init(&sendbuf, "", 0);
ptls_buffer_init(&recvbuf, "", 0);
// Perform handshake
int ret;
ptls_handshake_properties_t props = {};
// Start handshake
ret = ptls_handshake(tls, &sendbuf, NULL, 0, &props);
if (ret != PTLS_ERROR_IN_PROGRESS) {
fprintf(stderr, "Failed to initiate handshake: %d\n", ret);
return -1;
}
// Send initial handshake data
if (sendbuf.off > 0) {
send(sock, sendbuf.base, sendbuf.off, 0);
ptls_buffer_dispose(&sendbuf);
ptls_buffer_init(&sendbuf, "", 0);
}
// Continue handshake until complete
while (ptls_handshake_is_complete(tls) == 0) {
size_t received = recv(sock, buffer, BUFFER_SIZE, 0);
if (received == 0) {
fprintf(stderr, "Connection closed during handshake\n");
goto Exit;
}
ret = ptls_handshake(tls, &sendbuf, buffer, &received, &props);
if (ret != 0 && ret != PTLS_ERROR_IN_PROGRESS) {
fprintf(stderr, "Handshake failed: %d %d\n", ret, PTLS_ERROR_IN_PROGRESS);
goto Exit;
}
if (sendbuf.off > 0) {
send(sock, sendbuf.base, sendbuf.off, 0);
ptls_buffer_dispose(&sendbuf);
ptls_buffer_init(&sendbuf, "", 0);
}
}
printf("Handshake completed successfully\n");
// Send encrypted message
const char *message = "Hello, Server!";
ptls_buffer_t encbuf;
ptls_buffer_init(&encbuf, "", 0);
ret = ptls_send(tls, &encbuf, message, strlen(message));
if (ret != 0) {
fprintf(stderr, "Failed to encrypt message: %d\n", ret);
goto Exit;
}
send(sock, encbuf.base, encbuf.off, 0);
printf("Sent encrypted message: %s\n", message);
Exit:
ptls_buffer_dispose(&encbuf);
ptls_buffer_dispose(&sendbuf);
ptls_buffer_dispose(&recvbuf);
ptls_free(tls);
close(sock);
return 0;
}
// server.c
#include <picotls.h>
#include <picotls/openssl.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <openssl/pem.h>
// generate key
// openssl ecparam -name prime256v1 -genkey -noout -out server.key
// generate cert
// openssl req -new -x509 -key server.key -out server.crt -days 365 -subj "/CN=localhost"
// gcc server.c -o server libpicotls-core.a libpicotls-openssl.a -Iinclude -lcrypto
#define PORT 8443
#define BUFFER_SIZE 1024
int main(void)
{
int server_fd, client_fd;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
ptls_openssl_sign_certificate_t sign_cert;
// Load the private key
FILE *fp = fopen("server.key", "rb");
if (!fp) {
perror("Failed to open private key file");
return -1;
}
EVP_PKEY *pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
fclose(fp);
if (!pkey) {
perror("Failed to read private key");
return -1;
}
// Initialize the certificate signing structure
ptls_openssl_init_sign_certificate(&sign_cert, pkey);
EVP_PKEY_free(pkey); // Free the key, as it's now managed by sign_cert
ptls_context_t ctx = {
.random_bytes = ptls_openssl_random_bytes,
.get_time = &ptls_get_time,
.key_exchanges = ptls_openssl_key_exchanges,
.cipher_suites = ptls_openssl_cipher_suites,
.sign_certificate = &sign_cert.super,
};
// Load server certificate and key
ptls_load_certificates(&ctx, "server.crt");
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket creation failed");
return -1;
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("Setsockopt failed");
return -1;
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
return -1;
}
if (listen(server_fd, 3) < 0) {
perror("Listen failed");
return -1;
}
printf("Server listening on port %d...\n", PORT);
if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
perror("Accept failed");
return -1;
}
printf("Client connected\n");
// Create TLS connection
ptls_t *tls = ptls_new(&ctx, 1); // 1 for server
if (tls == NULL) {
fprintf(stderr, "Failed to create TLS context\n");
return -1;
}
ptls_buffer_t sendbuf, recvbuf;
ptls_buffer_init(&sendbuf, "", 0);
ptls_buffer_init(&recvbuf, "", 0);
// Handle handshake
int ret;
ptls_handshake_properties_t props = {};
int handshake_complete = 0;
while (!handshake_complete) {
size_t received = recv(client_fd, buffer, BUFFER_SIZE, 0);
if (received == 0) {
fprintf(stderr, "Connection closed during handshake\n");
goto Exit;
}
ret = ptls_handshake(tls, &sendbuf, buffer, &received, &props);
if (ret == 0) {
handshake_complete = 1;
} else if (ret != PTLS_ERROR_IN_PROGRESS) {
fprintf(stderr, "Handshake failed: %d\n", ret);
goto Exit;
}
if (sendbuf.off > 0) {
send(client_fd, sendbuf.base, sendbuf.off, 0);
ptls_buffer_dispose(&sendbuf);
ptls_buffer_init(&sendbuf, "", 0);
}
}
printf("Handshake completed successfully\n");
// Receive and decrypt data
while (1) {
size_t received = recv(client_fd, buffer, BUFFER_SIZE, 0);
if (received == 0) {
printf("Connection closed by client\n");
break;
}
ret = ptls_receive(tls, &recvbuf, buffer, received);
if (ret == 0) {
printf("Received: %.*s\n", (int)recvbuf.off, (char *)recvbuf.base);
ptls_buffer_dispose(&recvbuf);
ptls_buffer_init(&recvbuf, "", 0);
}
}
Exit:
ptls_buffer_dispose(&sendbuf);
ptls_buffer_dispose(&recvbuf);
ptls_free(tls);
close(client_fd);
close(server_fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment