commit
a627f04342
|
|
@ -287,6 +287,14 @@ pq/ml_dsa/ml_dsa_test
|
|||
pq/ml_dsa/*.bin
|
||||
pq/ml_dsa/*.key
|
||||
|
||||
# PQC crypto-proxy
|
||||
pq/pqc_proxy/pq-proxy
|
||||
pq/pqc_proxy/pq-client
|
||||
pq/pqc_proxy/legacy-server
|
||||
pq/pqc_proxy/upgrade-proxy
|
||||
pq/pqc_proxy/legacy-client
|
||||
pq/pqc_proxy/pq-server
|
||||
|
||||
embedded/tls-client-server
|
||||
embedded/tls-server-size
|
||||
embedded/tls-sock-client
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
# PQC crypto-proxy examples Makefile
|
||||
CC = gcc
|
||||
WOLFSSL_INSTALL_DIR = /usr/local
|
||||
CFLAGS = -Wall -I$(WOLFSSL_INSTALL_DIR)/include
|
||||
LIBS = -L$(WOLFSSL_INSTALL_DIR)/lib -lm
|
||||
|
||||
# option variables
|
||||
DYN_LIB = -lwolfssl
|
||||
STATIC_LIB = $(WOLFSSL_INSTALL_DIR)/lib/libwolfssl.a
|
||||
DEBUG_FLAGS = -g -DDEBUG
|
||||
OPTIMIZE = -Os
|
||||
|
||||
# Options
|
||||
#CFLAGS+=$(DEBUG_FLAGS)
|
||||
CFLAGS+=$(OPTIMIZE)
|
||||
#LIBS+=$(STATIC_LIB)
|
||||
LIBS+=$(DYN_LIB)
|
||||
|
||||
# build targets
|
||||
SRC=$(wildcard *.c)
|
||||
TARGETS=$(patsubst %.c, %, $(SRC))
|
||||
DEPS=proxy-common.h
|
||||
|
||||
.PHONY: clean all
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
debug: CFLAGS+=$(DEBUG_FLAGS)
|
||||
debug: all
|
||||
|
||||
# build template
|
||||
%: %.c $(DEPS)
|
||||
$(CC) -o $@ $< $(CFLAGS) $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGETS)
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
# wolfSSL Post-Quantum Crypto-Proxy Examples
|
||||
|
||||
This directory shows two ways to use a small TLS terminating proxy to add a
|
||||
**post-quantum (hybrid ML-KEM) key exchange** to a connection where one of the
|
||||
two endpoints can only speak **legacy, classical TLS**. In both cases the proxy
|
||||
decrypts on one side and re-encrypts on the other, so the post-quantum
|
||||
protection is added transparently and neither legacy endpoint has to change.
|
||||
|
||||
### Direction 1 — quantum-safe front door (`pq-proxy`)
|
||||
|
||||
A modern client reaches a legacy origin that cannot be upgraded. The
|
||||
client-facing leg is post-quantum; the origin-facing leg stays classical.
|
||||
|
||||
```
|
||||
┌────────────┐ PQC / hybrid TLS 1.3 ┌────────────┐ legacy TLS 1.2 ┌───────────────┐
|
||||
│ pq-client │ ──────────────────────► │ pq-proxy │ ────────────────► │ legacy-server │
|
||||
│ (modern) │ P256_ML_KEM_768 │ (terminates│ ECDHE-RSA-AES128 │ (origin) │
|
||||
└────────────┘ │ + re-TLS) │ -GCM-SHA256 └───────────────┘
|
||||
└────────────┘
|
||||
```
|
||||
|
||||
### Direction 2 — quantum-safe upgrade (`upgrade-proxy`)
|
||||
|
||||
A legacy client that cannot speak PQC reaches a modern origin. The proxy
|
||||
upgrades the connection so the leg crossing the untrusted network to the server
|
||||
is post-quantum.
|
||||
|
||||
```
|
||||
┌───────────────┐ legacy TLS 1.2 ┌──────────────┐ PQC / hybrid TLS 1.3 ┌───────────┐
|
||||
│ legacy-client │ ────────────────► │ upgrade-proxy│ ──────────────────────► │ pq-server │
|
||||
│ (legacy) │ ECDHE-RSA-AES128 │ (terminates │ P256_ML_KEM_768 │ (modern) │
|
||||
└───────────────┘ -GCM-SHA256 │ + re-TLS) │ └───────────┘
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
Each proxy is simultaneously a TLS **server** on one leg and a TLS **client**
|
||||
on the other.
|
||||
|
||||
## Why this is useful
|
||||
|
||||
A "harvest now, decrypt later" attacker can record TLS traffic today and break
|
||||
the (classical) key exchange once a quantum computer exists. Putting a
|
||||
post-quantum **hybrid** key exchange on the leg that crosses the public network
|
||||
removes that risk, even when one of the endpoints behind the proxy cannot yet
|
||||
be upgraded.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Role | Crypto |
|
||||
|------|------|--------|
|
||||
| `pq-proxy.c` | Direction 1 proxy: PQC frontend, legacy backend | frontend: TLS 1.3 hybrid ML-KEM, backend: TLS 1.2 ECDHE-RSA |
|
||||
| `legacy-server.c` | Direction 1 legacy origin | TLS 1.2 ECDHE-RSA-AES128-GCM-SHA256 |
|
||||
| `pq-client.c` | Direction 1 modern client | TLS 1.3 hybrid ML-KEM |
|
||||
| `upgrade-proxy.c` | Direction 2 proxy: legacy frontend, PQC backend | frontend: TLS 1.2 ECDHE-RSA, backend: TLS 1.3 hybrid ML-KEM |
|
||||
| `pq-server.c` | Direction 2 modern origin | TLS 1.3 hybrid ML-KEM |
|
||||
| `legacy-client.c` | Direction 2 legacy client | TLS 1.2 ECDHE-RSA-AES128-GCM-SHA256 |
|
||||
| `proxy-common.h` | Shared ports, groups, cert paths | — |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Build and install wolfSSL with ML-KEM (Kyber) and TLS 1.3:
|
||||
|
||||
```sh
|
||||
./configure --enable-mlkem --enable-tls13
|
||||
make
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
```
|
||||
|
||||
If you also build with `--enable-mldsa`, the PQC leg automatically upgrades to a
|
||||
**fully** post-quantum handshake (ML-DSA certificate + ML-KEM key exchange).
|
||||
Otherwise the PQC leg uses post-quantum key exchange with a classical RSA
|
||||
certificate for authentication — which is the most common real-world PQC
|
||||
deployment today, because public CAs do not yet issue PQC certificates.
|
||||
|
||||
## Building
|
||||
|
||||
```sh
|
||||
make
|
||||
```
|
||||
|
||||
## Running direction 1 (quantum-safe front door)
|
||||
|
||||
Use three terminals.
|
||||
|
||||
1. Start the legacy origin server (classical TLS 1.2):
|
||||
|
||||
```sh
|
||||
./legacy-server
|
||||
```
|
||||
|
||||
2. Start the crypto-proxy:
|
||||
|
||||
```sh
|
||||
./pq-proxy
|
||||
```
|
||||
|
||||
```
|
||||
PQC crypto-proxy ready
|
||||
frontend : 0.0.0.0:11111 TLS 1.3 kex=P256_ML_KEM_768 auth=RSA-2048 (classical)
|
||||
backend : 127.0.0.1:11112 TLS 1.2 ECDHE-RSA-AES128-GCM-SHA256
|
||||
```
|
||||
|
||||
3. Connect with the modern client and type a message:
|
||||
|
||||
```sh
|
||||
./pq-client 127.0.0.1
|
||||
```
|
||||
|
||||
```
|
||||
Connected to proxy: TLSv1.3 TLS_AES_256_GCM_SHA384 kex=SecP256r1MLKEM768
|
||||
Message for server: hello
|
||||
Server: [origin] legacy service reached over TLS 1.2
|
||||
```
|
||||
|
||||
The proxy reports both halves of the bridge:
|
||||
|
||||
```
|
||||
Bridging connection:
|
||||
frontend TLSv1.3 TLS_AES_256_GCM_SHA384 kex=SecP256r1MLKEM768
|
||||
backend TLSv1.2 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 kex=SECP256R1
|
||||
```
|
||||
|
||||
## Running direction 2 (quantum-safe upgrade)
|
||||
|
||||
Use three terminals.
|
||||
|
||||
1. Start the modern PQC origin server (TLS 1.3, hybrid ML-KEM):
|
||||
|
||||
```sh
|
||||
./pq-server
|
||||
```
|
||||
|
||||
2. Start the upgrade-proxy:
|
||||
|
||||
```sh
|
||||
./upgrade-proxy
|
||||
```
|
||||
|
||||
```
|
||||
PQC upgrade-proxy ready
|
||||
frontend : 0.0.0.0:22221 TLS 1.2 ECDHE-RSA-AES128-GCM-SHA256
|
||||
backend : 127.0.0.1:22222 TLS 1.3 kex=P256_ML_KEM_768 auth=RSA-2048 (classical)
|
||||
```
|
||||
|
||||
3. Connect with the legacy client and type a message:
|
||||
|
||||
```sh
|
||||
./legacy-client 127.0.0.1
|
||||
```
|
||||
|
||||
```
|
||||
Connected to proxy: TLSv1.2 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
Message for server: hello
|
||||
Server: [pq-server] reached over a quantum-safe TLS 1.3 link
|
||||
```
|
||||
|
||||
The proxy reports both halves of the bridge:
|
||||
|
||||
```
|
||||
Bridging connection:
|
||||
frontend TLSv1.2 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 kex=SECP256R1
|
||||
backend TLSv1.3 TLS_AES_256_GCM_SHA384 kex=SecP256r1MLKEM768
|
||||
```
|
||||
|
||||
In either direction, send the message `shutdown` to stop the origin server.
|
||||
|
||||
You can verify a proxy really requires PQC on its quantum-safe leg: it only ever
|
||||
negotiates the `P256_ML_KEM_768` group there (via `wolfSSL_CTX_set_groups()`),
|
||||
so a classical-only peer on that leg is rejected.
|
||||
|
||||
## Command-line options
|
||||
|
||||
```sh
|
||||
./legacy-server [<listen port>]
|
||||
./pq-proxy [<frontend port> [<backend host> [<backend port>]]]
|
||||
./pq-client [<proxy host> [<proxy port>]]
|
||||
|
||||
./pq-server [<listen port>]
|
||||
./upgrade-proxy [<frontend port> [<backend host> [<backend port>]]]
|
||||
./legacy-client [<proxy host> [<proxy port>]]
|
||||
```
|
||||
|
||||
The two directions use different default ports (11111/11112 and 22221/22222),
|
||||
so both demos can run at the same time.
|
||||
|
||||
## Customising the cryptography
|
||||
|
||||
All algorithm selection lives in `proxy-common.h`:
|
||||
|
||||
- **PQC group** — change `FRONTEND_GROUP` to raise the security level
|
||||
(e.g. `WOLFSSL_SECP384R1MLKEM1024`) or to use a non-hybrid, pure-PQC group
|
||||
(`WOLFSSL_ML_KEM_768`). Both proxies restrict negotiation on their PQC leg to
|
||||
this single group, so only quantum-safe peers are accepted there.
|
||||
- **Legacy cipher suite** — change `BACKEND_CIPHERS` to model whatever legacy
|
||||
suite your real endpoint requires.
|
||||
|
||||
## Notes and limitations
|
||||
|
||||
- For clarity each proxy handles **one client connection at a time**. A
|
||||
production proxy would `fork()` or spawn a thread per accepted connection (or
|
||||
use the non-blocking / event-loop patterns in `tls/server-tls-epoll-*.c`),
|
||||
reusing the two `WOLFSSL_CTX` objects across connections as these examples
|
||||
already do.
|
||||
- Data is relayed with a `select()` loop over the two socket descriptors,
|
||||
draining any records wolfSSL has already buffered with `wolfSSL_pending()`.
|
||||
- The example certificates come from the repository's `certs/` directory and
|
||||
are for testing only.
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/* legacy-client.c
|
||||
*
|
||||
* Copyright (C) 2006-2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* A stand-in for a legacy client that can only speak classical TLS 1.2. It
|
||||
* connects to the upgrade-proxy, which transparently re-establishes the request
|
||||
* to the modern pq-server over a quantum-safe (hybrid ML-KEM) TLS 1.3 link. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
|
||||
#include "proxy-common.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
int sockfd = SOCKET_INVALID;
|
||||
int port = UPGRADE_PORT;
|
||||
const char* host = "127.0.0.1";
|
||||
struct sockaddr_in servAddr;
|
||||
char buff[256];
|
||||
size_t len;
|
||||
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
WOLFSSL* ssl = NULL;
|
||||
|
||||
if (argc >= 2) host = argv[1];
|
||||
if (argc >= 3) port = atoi(argv[2]);
|
||||
if (argc > 3) {
|
||||
printf("usage: %s [<proxy host> [<proxy port>]]\n", argv[0]);
|
||||
printf("Defaults: 127.0.0.1 %d\n", UPGRADE_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to create the socket\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(&servAddr, 0, sizeof(servAddr));
|
||||
servAddr.sin_family = AF_INET;
|
||||
servAddr.sin_port = htons(port);
|
||||
if (inet_pton(AF_INET, host, &servAddr.sin_addr) != 1) {
|
||||
fprintf(stderr, "ERROR: invalid address %s\n", host);
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (connect(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to connect\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
wolfSSL_Init();
|
||||
|
||||
/* Deliberately pin to classical TLS 1.2 - this client cannot do PQC. */
|
||||
if ((ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((ret = wolfSSL_CTX_set_cipher_list(ctx, LEGACY_CIPHERS))
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to set cipher list %s\n",
|
||||
LEGACY_CIPHERS);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Trust the proxy's frontend (legacy) certificate. */
|
||||
if ((ret = wolfSSL_CTX_load_verify_locations(ctx, LEGACY_CA, NULL))
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", LEGACY_CA);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((ssl = wolfSSL_new(ctx)) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create WOLFSSL object\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
wolfSSL_set_fd(ssl, sockfd);
|
||||
|
||||
if (wolfSSL_connect(ssl) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: handshake failed: %d\n",
|
||||
wolfSSL_get_error(ssl, 0));
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("Connected to proxy: %s %s\n",
|
||||
wolfSSL_get_version(ssl), wolfSSL_get_cipher(ssl));
|
||||
|
||||
printf("Message for server: ");
|
||||
memset(buff, 0, sizeof(buff));
|
||||
if (fgets(buff, sizeof(buff), stdin) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to get message\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
len = strnlen(buff, sizeof(buff));
|
||||
|
||||
if (wolfSSL_write(ssl, buff, (int)len) != (int)len) {
|
||||
fprintf(stderr, "ERROR: failed to write\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(buff, 0, sizeof(buff));
|
||||
if (wolfSSL_read(ssl, buff, sizeof(buff) - 1) < 0) {
|
||||
fprintf(stderr, "ERROR: failed to read\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
printf("Server: %s", buff);
|
||||
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
if (ssl) {
|
||||
wolfSSL_shutdown(ssl);
|
||||
wolfSSL_free(ssl);
|
||||
}
|
||||
if (sockfd != SOCKET_INVALID)
|
||||
close(sockfd);
|
||||
if (ctx)
|
||||
wolfSSL_CTX_free(ctx);
|
||||
wolfSSL_Cleanup();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
/* legacy-server.c
|
||||
*
|
||||
* Copyright (C) 2006-2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* A stand-in for a legacy origin service: it only speaks classical TLS 1.2.
|
||||
* The PQC proxy (pq-proxy) terminates the quantum-safe client connection and
|
||||
* forwards the plaintext to this server over a legacy cipher suite. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
|
||||
#include "proxy-common.h"
|
||||
|
||||
static int mSockfd = SOCKET_INVALID;
|
||||
static int mConnd = SOCKET_INVALID;
|
||||
static int mShutdown = 0;
|
||||
|
||||
static void sig_handler(const int sig)
|
||||
{
|
||||
(void)sig;
|
||||
mShutdown = 1;
|
||||
if (mConnd != SOCKET_INVALID) {
|
||||
close(mConnd);
|
||||
mConnd = SOCKET_INVALID;
|
||||
}
|
||||
if (mSockfd != SOCKET_INVALID) {
|
||||
close(mSockfd);
|
||||
mSockfd = SOCKET_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/* Accept and service client connections until a shutdown is requested. */
|
||||
static int accept_loop(WOLFSSL_CTX* ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sockaddr_in clientAddr;
|
||||
socklen_t size = sizeof(clientAddr);
|
||||
char buff[256];
|
||||
const char* reply = "[origin] legacy service reached over TLS 1.2\n";
|
||||
WOLFSSL* ssl = NULL;
|
||||
|
||||
while (!mShutdown) {
|
||||
int n;
|
||||
|
||||
printf("Waiting for a connection...\n");
|
||||
if ((mConnd = accept(mSockfd, (struct sockaddr*)&clientAddr, &size))
|
||||
== -1) {
|
||||
if (mShutdown)
|
||||
break;
|
||||
fprintf(stderr, "ERROR: failed to accept the connection\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ssl = wolfSSL_new(ctx)) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create WOLFSSL object\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
wolfSSL_set_fd(ssl, mConnd);
|
||||
|
||||
if (wolfSSL_accept(ssl) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: wolfSSL_accept error %d\n",
|
||||
wolfSSL_get_error(ssl, 0));
|
||||
goto conn_cleanup;
|
||||
}
|
||||
|
||||
printf("Proxy connected: %s %s\n",
|
||||
wolfSSL_get_version(ssl), wolfSSL_get_cipher(ssl));
|
||||
|
||||
memset(buff, 0, sizeof(buff));
|
||||
if ((n = wolfSSL_read(ssl, buff, sizeof(buff) - 1)) > 0) {
|
||||
printf("Received: %s", buff);
|
||||
if (strncmp(buff, "shutdown", 8) == 0) {
|
||||
printf("Shutdown command issued!\n");
|
||||
mShutdown = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (wolfSSL_write(ssl, reply, (int)strlen(reply)) < 0)
|
||||
fprintf(stderr, "ERROR: failed to write\n");
|
||||
|
||||
wolfSSL_shutdown(ssl);
|
||||
|
||||
conn_cleanup:
|
||||
wolfSSL_free(ssl);
|
||||
ssl = NULL;
|
||||
if (mConnd != SOCKET_INVALID) {
|
||||
close(mConnd);
|
||||
mConnd = SOCKET_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
int port = BACKEND_PORT;
|
||||
struct sockaddr_in servAddr;
|
||||
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
|
||||
if (argc == 2) {
|
||||
port = atoi(argv[1]);
|
||||
}
|
||||
else if (argc != 1) {
|
||||
printf("usage: %s [<listen port>]\n", argv[0]);
|
||||
printf("Default listen port: %d\n", BACKEND_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
wolfSSL_Init();
|
||||
|
||||
if ((mSockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to create the socket\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Deliberately pin to classical TLS 1.2 - cannot be upgraded. */
|
||||
if ((ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((ret = wolfSSL_CTX_set_cipher_list(ctx, BACKEND_CIPHERS))
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to set cipher list %s\n",
|
||||
BACKEND_CIPHERS);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((ret = wolfSSL_CTX_use_certificate_file(ctx, BACKEND_CERT,
|
||||
WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", BACKEND_CERT);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((ret = wolfSSL_CTX_use_PrivateKey_file(ctx, BACKEND_KEY,
|
||||
WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", BACKEND_KEY);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(&servAddr, 0, sizeof(servAddr));
|
||||
servAddr.sin_family = AF_INET;
|
||||
servAddr.sin_port = htons(port);
|
||||
servAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(mSockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to bind\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (listen(mSockfd, 5) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to listen\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("Legacy origin server listening on port %d (%s)\n",
|
||||
port, BACKEND_CIPHERS);
|
||||
|
||||
ret = accept_loop(ctx);
|
||||
if (ret == 0)
|
||||
printf("Shutdown complete\n");
|
||||
|
||||
exit:
|
||||
if (mConnd != SOCKET_INVALID)
|
||||
close(mConnd);
|
||||
if (mSockfd != SOCKET_INVALID)
|
||||
close(mSockfd);
|
||||
if (ctx)
|
||||
wolfSSL_CTX_free(ctx);
|
||||
wolfSSL_Cleanup();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
/* pq-client.c
|
||||
*
|
||||
* Copyright (C) 2006-2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* A modern client that connects to the PQC crypto-proxy using a post-quantum
|
||||
* (hybrid ML-KEM) TLS 1.3 handshake. It neither knows nor cares that the proxy
|
||||
* forwards its request to a legacy TLS 1.2 origin behind the scenes. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
|
||||
#include "proxy-common.h"
|
||||
|
||||
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM)
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
int sockfd = SOCKET_INVALID;
|
||||
int port = FRONTEND_PORT;
|
||||
const char* host = "127.0.0.1";
|
||||
struct sockaddr_in servAddr;
|
||||
char buff[256];
|
||||
size_t len;
|
||||
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
WOLFSSL* ssl = NULL;
|
||||
|
||||
if (argc >= 2) host = argv[1];
|
||||
if (argc >= 3) port = atoi(argv[2]);
|
||||
if (argc > 3) {
|
||||
printf("usage: %s [<proxy host> [<proxy port>]]\n", argv[0]);
|
||||
printf("Defaults: 127.0.0.1 %d\n", FRONTEND_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to create the socket\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(&servAddr, 0, sizeof(servAddr));
|
||||
servAddr.sin_family = AF_INET;
|
||||
servAddr.sin_port = htons(port);
|
||||
if (inet_pton(AF_INET, host, &servAddr.sin_addr) != 1) {
|
||||
fprintf(stderr, "ERROR: invalid address %s\n", host);
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (connect(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to connect\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
wolfSSL_Init();
|
||||
|
||||
if ((ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Trust the proxy's frontend certificate. */
|
||||
if ((ret = wolfSSL_CTX_load_verify_locations(ctx, FRONTEND_CA, NULL))
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", FRONTEND_CA);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((ssl = wolfSSL_new(ctx)) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create WOLFSSL object\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Offer the post-quantum (hybrid) group for key establishment. */
|
||||
if ((ret = wolfSSL_UseKeyShare(ssl, FRONTEND_GROUP)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to set PQC key share (%s)\n",
|
||||
FRONTEND_GROUP_NAME);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
wolfSSL_set_fd(ssl, sockfd);
|
||||
|
||||
if (wolfSSL_connect(ssl) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: handshake failed: %d\n",
|
||||
wolfSSL_get_error(ssl, 0));
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("Connected to proxy: %s %s kex=%s\n",
|
||||
wolfSSL_get_version(ssl), wolfSSL_get_cipher(ssl),
|
||||
wolfSSL_get_curve_name(ssl));
|
||||
|
||||
printf("Message for server: ");
|
||||
memset(buff, 0, sizeof(buff));
|
||||
if (fgets(buff, sizeof(buff), stdin) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to get message\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
len = strnlen(buff, sizeof(buff));
|
||||
|
||||
if (wolfSSL_write(ssl, buff, (int)len) != (int)len) {
|
||||
fprintf(stderr, "ERROR: failed to write\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(buff, 0, sizeof(buff));
|
||||
if (wolfSSL_read(ssl, buff, sizeof(buff) - 1) < 0) {
|
||||
fprintf(stderr, "ERROR: failed to read\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
printf("Server: %s", buff);
|
||||
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
if (ssl) {
|
||||
wolfSSL_shutdown(ssl);
|
||||
wolfSSL_free(ssl);
|
||||
}
|
||||
if (sockfd != SOCKET_INVALID)
|
||||
close(sockfd);
|
||||
if (ctx)
|
||||
wolfSSL_CTX_free(ctx);
|
||||
wolfSSL_Cleanup();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("This example requires TLS 1.3 and ML-KEM (Kyber).\n");
|
||||
printf("Configure wolfSSL like this:\n");
|
||||
printf(" ./configure --enable-kyber --enable-tls13\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM */
|
||||
|
|
@ -0,0 +1,365 @@
|
|||
/* pq-proxy.c
|
||||
*
|
||||
* Copyright (C) 2006-2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* pq-proxy: a TLS terminating crypto-proxy.
|
||||
*
|
||||
* client =(PQC/hybrid TLS 1.3)=> pq-proxy =(legacy TLS 1.2)=> origin
|
||||
*
|
||||
* The proxy is simultaneously:
|
||||
* - a TLS *server* on the frontend, accepting a quantum-safe (hybrid ML-KEM)
|
||||
* handshake from modern clients;
|
||||
* - a TLS *client* on the backend, opening a classical TLS 1.2 connection to
|
||||
* a legacy origin server that cannot be upgraded.
|
||||
*
|
||||
* Application data is decrypted on one side and re-encrypted on the other, so
|
||||
* the post-quantum protection is added transparently in front of the legacy
|
||||
* service. Connections are handled one at a time to keep the example readable;
|
||||
* see the README for notes on serving clients concurrently.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
|
||||
#include "proxy-common.h"
|
||||
|
||||
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM)
|
||||
|
||||
static int mListenfd = SOCKET_INVALID;
|
||||
static int mShutdown = 0;
|
||||
|
||||
static void sig_handler(const int sig)
|
||||
{
|
||||
(void)sig;
|
||||
mShutdown = 1;
|
||||
if (mListenfd != SOCKET_INVALID) {
|
||||
close(mListenfd);
|
||||
mListenfd = SOCKET_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/* Open a TCP connection to the legacy origin server. */
|
||||
static int connect_backend(const char* host, int port)
|
||||
{
|
||||
int sockfd;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
return -1;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (inet_pton(AF_INET, host, &addr.sin_addr) != 1) {
|
||||
close(sockfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
close(sockfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
/* Pump all currently available plaintext from src to dst.
|
||||
* Returns 1 on success, 0 if the source closed, -1 on error. */
|
||||
static int pump(WOLFSSL* src, WOLFSSL* dst)
|
||||
{
|
||||
char buff[PROXY_BUFFER_SZ];
|
||||
int n;
|
||||
|
||||
do {
|
||||
n = wolfSSL_read(src, buff, sizeof(buff));
|
||||
if (n <= 0) {
|
||||
int err = wolfSSL_get_error(src, n);
|
||||
if (err == WOLFSSL_ERROR_WANT_READ ||
|
||||
err == WOLFSSL_ERROR_WANT_WRITE)
|
||||
return 1;
|
||||
return (n == 0 || err == WOLFSSL_ERROR_ZERO_RETURN) ? 0 : -1;
|
||||
}
|
||||
|
||||
if (wolfSSL_write(dst, buff, n) != n)
|
||||
return -1;
|
||||
|
||||
/* wolfSSL may already hold further decrypted records that the socket
|
||||
* select() would not report; drain while data remains buffered. */
|
||||
} while (wolfSSL_pending(src) > 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Shuttle application data between the frontend and backend TLS sessions until
|
||||
* either side closes the connection. */
|
||||
static void relay(WOLFSSL* front, int frontFd, WOLFSSL* back, int backFd)
|
||||
{
|
||||
int maxFd = (frontFd > backFd ? frontFd : backFd) + 1;
|
||||
|
||||
while (!mShutdown) {
|
||||
fd_set readfds;
|
||||
int rc;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(frontFd, &readfds);
|
||||
FD_SET(backFd, &readfds);
|
||||
|
||||
rc = select(maxFd, &readfds, NULL, NULL, NULL);
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
if (FD_ISSET(frontFd, &readfds) || wolfSSL_pending(front) > 0) {
|
||||
if (pump(front, back) <= 0)
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET(backFd, &readfds) || wolfSSL_pending(back) > 0) {
|
||||
if (pump(back, front) <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_leg(const char* label, WOLFSSL* ssl)
|
||||
{
|
||||
const char* group = wolfSSL_get_curve_name(ssl);
|
||||
printf(" %-9s %s %s kex=%s\n", label,
|
||||
wolfSSL_get_version(ssl), wolfSSL_get_cipher(ssl),
|
||||
group ? group : "(classical)");
|
||||
}
|
||||
|
||||
/* Accept clients on the frontend and bridge each one to a fresh backend
|
||||
* connection until a shutdown is requested. */
|
||||
static int accept_loop(WOLFSSL_CTX* frontCtx, WOLFSSL_CTX* backCtx,
|
||||
const char* backHost, int backPort)
|
||||
{
|
||||
struct sockaddr_in clientAddr;
|
||||
socklen_t size = sizeof(clientAddr);
|
||||
|
||||
while (!mShutdown) {
|
||||
int frontFd = SOCKET_INVALID;
|
||||
int backFd = SOCKET_INVALID;
|
||||
WOLFSSL* front = NULL;
|
||||
WOLFSSL* back = NULL;
|
||||
|
||||
printf("\nWaiting for a client...\n");
|
||||
frontFd = accept(mListenfd, (struct sockaddr*)&clientAddr, &size);
|
||||
if (frontFd == -1) {
|
||||
if (mShutdown)
|
||||
break;
|
||||
fprintf(stderr, "ERROR: failed to accept\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Terminate the quantum-safe client connection. */
|
||||
if ((front = wolfSSL_new(frontCtx)) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create frontend WOLFSSL\n");
|
||||
goto conn_cleanup;
|
||||
}
|
||||
wolfSSL_set_fd(front, frontFd);
|
||||
if (wolfSSL_accept(front) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: frontend handshake failed: %d\n",
|
||||
wolfSSL_get_error(front, 0));
|
||||
goto conn_cleanup;
|
||||
}
|
||||
|
||||
/* Open the legacy connection to the origin. */
|
||||
backFd = connect_backend(backHost, backPort);
|
||||
if (backFd < 0) {
|
||||
fprintf(stderr, "ERROR: failed to reach backend %s:%d\n",
|
||||
backHost, backPort);
|
||||
goto conn_cleanup;
|
||||
}
|
||||
if ((back = wolfSSL_new(backCtx)) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create backend WOLFSSL\n");
|
||||
goto conn_cleanup;
|
||||
}
|
||||
wolfSSL_set_fd(back, backFd);
|
||||
if (wolfSSL_connect(back) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: backend handshake failed: %d\n",
|
||||
wolfSSL_get_error(back, 0));
|
||||
goto conn_cleanup;
|
||||
}
|
||||
|
||||
printf("Bridging connection:\n");
|
||||
print_leg("frontend", front);
|
||||
print_leg("backend", back);
|
||||
|
||||
relay(front, frontFd, back, backFd);
|
||||
printf("Connection closed\n");
|
||||
|
||||
conn_cleanup:
|
||||
if (front) {
|
||||
wolfSSL_shutdown(front);
|
||||
wolfSSL_free(front);
|
||||
}
|
||||
if (back) {
|
||||
wolfSSL_shutdown(back);
|
||||
wolfSSL_free(back);
|
||||
}
|
||||
if (frontFd != SOCKET_INVALID)
|
||||
close(frontFd);
|
||||
if (backFd != SOCKET_INVALID)
|
||||
close(backFd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
int frontPort = FRONTEND_PORT;
|
||||
const char* backHost = BACKEND_HOST;
|
||||
int backPort = BACKEND_PORT;
|
||||
int on = 1;
|
||||
|
||||
struct sockaddr_in servAddr;
|
||||
|
||||
/* The frontend group is restricted to a single post-quantum (hybrid) group
|
||||
* so that the proxy will only accept quantum-safe key exchanges. */
|
||||
int frontGroups[] = { FRONTEND_GROUP };
|
||||
|
||||
WOLFSSL_CTX* frontCtx = NULL; /* frontend: PQC TLS 1.3 server */
|
||||
WOLFSSL_CTX* backCtx = NULL; /* backend: legacy TLS 1.2 client */
|
||||
|
||||
if (argc >= 2) frontPort = atoi(argv[1]);
|
||||
if (argc >= 3) backHost = argv[2];
|
||||
if (argc >= 4) backPort = atoi(argv[3]);
|
||||
if (argc > 4) {
|
||||
printf("usage: %s [<frontend port> [<backend host> "
|
||||
"[<backend port>]]]\n", argv[0]);
|
||||
printf("Defaults: %d %s %d\n", FRONTEND_PORT, BACKEND_HOST,
|
||||
BACKEND_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
wolfSSL_Init();
|
||||
|
||||
/* ---- Frontend context: post-quantum / hybrid TLS 1.3 server ---------- */
|
||||
if ((frontCtx = wolfSSL_CTX_new(wolfTLSv1_3_server_method())) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create frontend WOLFSSL_CTX\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_use_certificate_file(frontCtx, FRONTEND_CERT,
|
||||
WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", FRONTEND_CERT);
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_use_PrivateKey_file(frontCtx, FRONTEND_KEY,
|
||||
WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", FRONTEND_KEY);
|
||||
goto exit;
|
||||
}
|
||||
/* Only negotiate the hybrid PQC group - reject classical-only clients. */
|
||||
if ((ret = wolfSSL_CTX_set_groups(frontCtx, frontGroups,
|
||||
sizeof(frontGroups) / sizeof(frontGroups[0]))) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to set frontend PQC group\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* ---- Backend context: legacy classical TLS 1.2 client ---------------- */
|
||||
if ((backCtx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create backend WOLFSSL_CTX\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_set_cipher_list(backCtx, BACKEND_CIPHERS))
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to set backend cipher list\n");
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_load_verify_locations(backCtx, BACKEND_CA, NULL))
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", BACKEND_CA);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* ---- Listen on the frontend port ------------------------------------- */
|
||||
if ((mListenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to create the socket\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
setsockopt(mListenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
memset(&servAddr, 0, sizeof(servAddr));
|
||||
servAddr.sin_family = AF_INET;
|
||||
servAddr.sin_port = htons(frontPort);
|
||||
servAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(mListenfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to bind\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
if (listen(mListenfd, 5) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to listen\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("PQC crypto-proxy ready\n");
|
||||
printf(" frontend : 0.0.0.0:%d TLS 1.3 kex=%s auth=%s\n",
|
||||
frontPort, FRONTEND_GROUP_NAME, FRONTEND_AUTH);
|
||||
printf(" backend : %s:%d TLS 1.2 %s\n",
|
||||
backHost, backPort, BACKEND_CIPHERS);
|
||||
|
||||
ret = accept_loop(frontCtx, backCtx, backHost, backPort);
|
||||
|
||||
printf("Shutdown complete\n");
|
||||
|
||||
exit:
|
||||
if (mListenfd != SOCKET_INVALID)
|
||||
close(mListenfd);
|
||||
if (frontCtx)
|
||||
wolfSSL_CTX_free(frontCtx);
|
||||
if (backCtx)
|
||||
wolfSSL_CTX_free(backCtx);
|
||||
wolfSSL_Cleanup();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("This example requires TLS 1.3 and ML-KEM (Kyber).\n");
|
||||
printf("Configure wolfSSL like this:\n");
|
||||
printf(" ./configure --enable-kyber --enable-tls13\n");
|
||||
printf("Add --enable-dilithium for post-quantum authentication too.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM */
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
/* pq-server.c
|
||||
*
|
||||
* Copyright (C) 2006-2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* A modern origin service that only accepts post-quantum (hybrid ML-KEM)
|
||||
* TLS 1.3 connections. The upgrade-proxy (upgrade-proxy) connects here on
|
||||
* behalf of legacy clients that cannot speak PQC themselves. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
|
||||
#include "proxy-common.h"
|
||||
|
||||
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM)
|
||||
|
||||
static int mSockfd = SOCKET_INVALID;
|
||||
static int mConnd = SOCKET_INVALID;
|
||||
static int mShutdown = 0;
|
||||
|
||||
static void sig_handler(const int sig)
|
||||
{
|
||||
(void)sig;
|
||||
mShutdown = 1;
|
||||
if (mConnd != SOCKET_INVALID) {
|
||||
close(mConnd);
|
||||
mConnd = SOCKET_INVALID;
|
||||
}
|
||||
if (mSockfd != SOCKET_INVALID) {
|
||||
close(mSockfd);
|
||||
mSockfd = SOCKET_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/* Accept and service client connections until a shutdown is requested. */
|
||||
static int accept_loop(WOLFSSL_CTX* ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sockaddr_in clientAddr;
|
||||
socklen_t size = sizeof(clientAddr);
|
||||
char buff[256];
|
||||
const char* reply =
|
||||
"[pq-server] reached over a quantum-safe TLS 1.3 link\n";
|
||||
WOLFSSL* ssl = NULL;
|
||||
|
||||
while (!mShutdown) {
|
||||
int n;
|
||||
|
||||
printf("Waiting for a connection...\n");
|
||||
if ((mConnd = accept(mSockfd, (struct sockaddr*)&clientAddr, &size))
|
||||
== -1) {
|
||||
if (mShutdown)
|
||||
break;
|
||||
fprintf(stderr, "ERROR: failed to accept the connection\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ssl = wolfSSL_new(ctx)) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create WOLFSSL object\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
wolfSSL_set_fd(ssl, mConnd);
|
||||
|
||||
if (wolfSSL_accept(ssl) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: wolfSSL_accept error %d\n",
|
||||
wolfSSL_get_error(ssl, 0));
|
||||
goto conn_cleanup;
|
||||
}
|
||||
|
||||
printf("Proxy connected: %s %s kex=%s\n",
|
||||
wolfSSL_get_version(ssl), wolfSSL_get_cipher(ssl),
|
||||
wolfSSL_get_curve_name(ssl));
|
||||
|
||||
memset(buff, 0, sizeof(buff));
|
||||
if ((n = wolfSSL_read(ssl, buff, sizeof(buff) - 1)) > 0) {
|
||||
printf("Received: %s", buff);
|
||||
if (strncmp(buff, "shutdown", 8) == 0) {
|
||||
printf("Shutdown command issued!\n");
|
||||
mShutdown = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (wolfSSL_write(ssl, reply, (int)strlen(reply)) < 0)
|
||||
fprintf(stderr, "ERROR: failed to write\n");
|
||||
|
||||
wolfSSL_shutdown(ssl);
|
||||
|
||||
conn_cleanup:
|
||||
wolfSSL_free(ssl);
|
||||
ssl = NULL;
|
||||
if (mConnd != SOCKET_INVALID) {
|
||||
close(mConnd);
|
||||
mConnd = SOCKET_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
int port = PQSERVER_PORT;
|
||||
struct sockaddr_in servAddr;
|
||||
|
||||
/* Only negotiate the hybrid PQC group - reject classical-only peers. */
|
||||
int groups[] = { PQC_GROUP };
|
||||
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
|
||||
if (argc == 2) {
|
||||
port = atoi(argv[1]);
|
||||
}
|
||||
else if (argc != 1) {
|
||||
printf("usage: %s [<listen port>]\n", argv[0]);
|
||||
printf("Default listen port: %d\n", PQSERVER_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
wolfSSL_Init();
|
||||
|
||||
if ((mSockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to create the socket\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method())) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((ret = wolfSSL_CTX_use_certificate_file(ctx, PQC_CERT,
|
||||
WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", PQC_CERT);
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_use_PrivateKey_file(ctx, PQC_KEY,
|
||||
WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", PQC_KEY);
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_set_groups(ctx, groups,
|
||||
sizeof(groups) / sizeof(groups[0]))) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to set PQC group\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(&servAddr, 0, sizeof(servAddr));
|
||||
servAddr.sin_family = AF_INET;
|
||||
servAddr.sin_port = htons(port);
|
||||
servAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(mSockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to bind\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
if (listen(mSockfd, 5) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to listen\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("PQC origin server listening on port %d\n", port);
|
||||
printf(" TLS 1.3 kex=%s auth=%s\n", PQC_GROUP_NAME, PQC_AUTH);
|
||||
|
||||
ret = accept_loop(ctx);
|
||||
if (ret == 0)
|
||||
printf("Shutdown complete\n");
|
||||
|
||||
exit:
|
||||
if (mConnd != SOCKET_INVALID)
|
||||
close(mConnd);
|
||||
if (mSockfd != SOCKET_INVALID)
|
||||
close(mSockfd);
|
||||
if (ctx)
|
||||
wolfSSL_CTX_free(ctx);
|
||||
wolfSSL_Cleanup();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("This example requires TLS 1.3 and ML-KEM (Kyber).\n");
|
||||
printf("Configure wolfSSL like this:\n");
|
||||
printf(" ./configure --enable-mlkem --enable-tls13\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM */
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/* proxy-common.h
|
||||
*
|
||||
* Copyright (C) 2006-2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* Shared configuration for the PQC crypto-proxy examples.
|
||||
*
|
||||
* Two proxy directions are demonstrated:
|
||||
*
|
||||
* 1. Quantum-safe front door (pq-proxy):
|
||||
* pq-client =(PQC TLS 1.3)=> pq-proxy =(legacy TLS 1.2)=> legacy-server
|
||||
*
|
||||
* 2. Quantum-safe upgrade (upgrade-proxy):
|
||||
* legacy-client =(legacy TLS 1.2)=> upgrade-proxy =(PQC TLS 1.3)=> pq-server
|
||||
*
|
||||
* In direction 1 the modern client gets a post-quantum key exchange while the
|
||||
* legacy origin is left untouched. In direction 2 a legacy client that cannot
|
||||
* speak PQC is transparently upgraded to a quantum-safe connection to a modern
|
||||
* server. Either way the post-quantum (hybrid) key exchange protects the leg
|
||||
* crossing the untrusted network against "harvest now, decrypt later" attacks.
|
||||
*
|
||||
* For direction 1 the macros below are named by position (FRONTEND_* is the
|
||||
* PQC leg, BACKEND_* is the legacy leg). The neutral PQC_* / LEGACY_* aliases
|
||||
* near the end of this file let direction 2 refer to each leg by its
|
||||
* cryptography instead of its position.
|
||||
*/
|
||||
|
||||
#ifndef PROXY_COMMON_H
|
||||
#define PROXY_COMMON_H
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
|
||||
/* Ports used by the examples. */
|
||||
#define FRONTEND_PORT 11111 /* dir 1: clients connect to pq-proxy */
|
||||
#define BACKEND_PORT 11112 /* dir 1: the legacy origin listens here */
|
||||
#define BACKEND_HOST "127.0.0.1"
|
||||
|
||||
#define UPGRADE_PORT 22221 /* dir 2: legacy clients connect here */
|
||||
#define PQSERVER_PORT 22222 /* dir 2: the PQC origin listens here */
|
||||
#define PQSERVER_HOST "127.0.0.1"
|
||||
|
||||
/* ---- Frontend: post-quantum / hybrid TLS 1.3 ----------------------------- */
|
||||
|
||||
/* Hybrid key exchange: X25519-style ECDHE (here NIST P-256) combined with
|
||||
* ML-KEM-768. The classical half keeps interop guarantees while the ML-KEM
|
||||
* half provides the post-quantum protection. Override at build time with
|
||||
* -DFRONTEND_GROUP=WOLFSSL_SECP384R1MLKEM1024 etc. for a higher security
|
||||
* level, or a pure WOLFSSL_ML_KEM_768 group for non-hybrid PQC. */
|
||||
#ifndef FRONTEND_GROUP
|
||||
#define FRONTEND_GROUP WOLFSSL_SECP256R1MLKEM768
|
||||
#define FRONTEND_GROUP_NAME "P256_ML_KEM_768"
|
||||
#endif
|
||||
#ifndef FRONTEND_GROUP_NAME
|
||||
#define FRONTEND_GROUP_NAME "(custom)"
|
||||
#endif
|
||||
|
||||
/* Server authentication on the frontend.
|
||||
*
|
||||
* If the wolfSSL library was built with ML-DSA (Dilithium) support we use a
|
||||
* post-quantum certificate chain so that BOTH key exchange and authentication
|
||||
* are quantum-safe. Otherwise we fall back to a classical RSA certificate,
|
||||
* which still gives a post-quantum *key exchange* - the most common real-world
|
||||
* PQC deployment today, since CAs do not yet issue PQC certificates. */
|
||||
#ifdef HAVE_DILITHIUM
|
||||
#define FRONTEND_CERT "../../certs/mldsa65_entity_cert.pem"
|
||||
#define FRONTEND_KEY "../../certs/mldsa65_entity_key.pem"
|
||||
#define FRONTEND_CA "../../certs/mldsa65_root_cert.pem"
|
||||
#define FRONTEND_AUTH "ML-DSA-65 (post-quantum)"
|
||||
#else
|
||||
#define FRONTEND_CERT "../../certs/server-cert.pem"
|
||||
#define FRONTEND_KEY "../../certs/server-key.pem"
|
||||
#define FRONTEND_CA "../../certs/ca-cert.pem"
|
||||
#define FRONTEND_AUTH "RSA-2048 (classical)"
|
||||
#endif
|
||||
|
||||
/* ---- Backend: legacy classical TLS 1.2 ----------------------------------- */
|
||||
|
||||
/* A deliberately old-school, non-PQC cipher suite. ECDHE-RSA still gives
|
||||
* forward secrecy but offers no protection against a quantum adversary. */
|
||||
#define BACKEND_CIPHERS "ECDHE-RSA-AES128-GCM-SHA256"
|
||||
#define BACKEND_CA "../../certs/ca-cert.pem"
|
||||
#define BACKEND_CERT "../../certs/server-cert.pem"
|
||||
#define BACKEND_KEY "../../certs/server-key.pem"
|
||||
|
||||
/* ---- Neutral aliases ----------------------------------------------------- */
|
||||
|
||||
/* Refer to each leg by its cryptography rather than its position, so the
|
||||
* upgrade-proxy (legacy frontend, PQC backend) reads naturally too. */
|
||||
#define PQC_GROUP FRONTEND_GROUP
|
||||
#define PQC_GROUP_NAME FRONTEND_GROUP_NAME
|
||||
#define PQC_CERT FRONTEND_CERT
|
||||
#define PQC_KEY FRONTEND_KEY
|
||||
#define PQC_CA FRONTEND_CA
|
||||
#define PQC_AUTH FRONTEND_AUTH
|
||||
|
||||
#define LEGACY_CIPHERS BACKEND_CIPHERS
|
||||
#define LEGACY_CERT BACKEND_CERT
|
||||
#define LEGACY_KEY BACKEND_KEY
|
||||
#define LEGACY_CA BACKEND_CA
|
||||
|
||||
#define PROXY_BUFFER_SZ 4096
|
||||
|
||||
#endif /* PROXY_COMMON_H */
|
||||
|
|
@ -0,0 +1,370 @@
|
|||
/* upgrade-proxy.c
|
||||
*
|
||||
* Copyright (C) 2006-2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* upgrade-proxy: a TLS terminating crypto-proxy that *upgrades* legacy clients.
|
||||
*
|
||||
* legacy-client =(legacy TLS 1.2)=> upgrade-proxy =(PQC TLS 1.3)=> pq-server
|
||||
*
|
||||
* This is the mirror image of pq-proxy. Here the proxy is:
|
||||
* - a TLS *server* on the frontend, accepting a classical TLS 1.2 handshake
|
||||
* from a legacy client that cannot speak PQC;
|
||||
* - a TLS *client* on the backend, opening a quantum-safe (hybrid ML-KEM)
|
||||
* TLS 1.3 connection to a modern origin server.
|
||||
*
|
||||
* It lets an un-upgradeable client still reach a service over a quantum-safe
|
||||
* link, with the post-quantum key exchange protecting the proxy-to-server leg.
|
||||
* Connections are handled one at a time to keep the example readable.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
|
||||
#include "proxy-common.h"
|
||||
|
||||
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM)
|
||||
|
||||
static int mListenfd = SOCKET_INVALID;
|
||||
static int mShutdown = 0;
|
||||
|
||||
static void sig_handler(const int sig)
|
||||
{
|
||||
(void)sig;
|
||||
mShutdown = 1;
|
||||
if (mListenfd != SOCKET_INVALID) {
|
||||
close(mListenfd);
|
||||
mListenfd = SOCKET_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/* Open a TCP connection to the PQC origin server. */
|
||||
static int connect_backend(const char* host, int port)
|
||||
{
|
||||
int sockfd;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
return -1;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (inet_pton(AF_INET, host, &addr.sin_addr) != 1) {
|
||||
close(sockfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
close(sockfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
/* Pump all currently available plaintext from src to dst.
|
||||
* Returns 1 on success, 0 if the source closed, -1 on error. */
|
||||
static int pump(WOLFSSL* src, WOLFSSL* dst)
|
||||
{
|
||||
char buff[PROXY_BUFFER_SZ];
|
||||
int n;
|
||||
|
||||
do {
|
||||
n = wolfSSL_read(src, buff, sizeof(buff));
|
||||
if (n <= 0) {
|
||||
int err = wolfSSL_get_error(src, n);
|
||||
if (err == WOLFSSL_ERROR_WANT_READ ||
|
||||
err == WOLFSSL_ERROR_WANT_WRITE)
|
||||
return 1;
|
||||
return (n == 0 || err == WOLFSSL_ERROR_ZERO_RETURN) ? 0 : -1;
|
||||
}
|
||||
|
||||
if (wolfSSL_write(dst, buff, n) != n)
|
||||
return -1;
|
||||
|
||||
/* wolfSSL may already hold further decrypted records that the socket
|
||||
* select() would not report; drain while data remains buffered. */
|
||||
} while (wolfSSL_pending(src) > 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Shuttle application data between the frontend and backend TLS sessions until
|
||||
* either side closes the connection. */
|
||||
static void relay(WOLFSSL* front, int frontFd, WOLFSSL* back, int backFd)
|
||||
{
|
||||
int maxFd = (frontFd > backFd ? frontFd : backFd) + 1;
|
||||
|
||||
while (!mShutdown) {
|
||||
fd_set readfds;
|
||||
int rc;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(frontFd, &readfds);
|
||||
FD_SET(backFd, &readfds);
|
||||
|
||||
rc = select(maxFd, &readfds, NULL, NULL, NULL);
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
if (FD_ISSET(frontFd, &readfds) || wolfSSL_pending(front) > 0) {
|
||||
if (pump(front, back) <= 0)
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET(backFd, &readfds) || wolfSSL_pending(back) > 0) {
|
||||
if (pump(back, front) <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_leg(const char* label, WOLFSSL* ssl)
|
||||
{
|
||||
const char* group = wolfSSL_get_curve_name(ssl);
|
||||
printf(" %-9s %s %s kex=%s\n", label,
|
||||
wolfSSL_get_version(ssl), wolfSSL_get_cipher(ssl),
|
||||
group ? group : "(classical)");
|
||||
}
|
||||
|
||||
/* Accept legacy clients on the frontend and bridge each one to a fresh
|
||||
* quantum-safe backend connection until a shutdown is requested. */
|
||||
static int accept_loop(WOLFSSL_CTX* frontCtx, WOLFSSL_CTX* backCtx,
|
||||
const char* backHost, int backPort)
|
||||
{
|
||||
struct sockaddr_in clientAddr;
|
||||
socklen_t size = sizeof(clientAddr);
|
||||
|
||||
while (!mShutdown) {
|
||||
int frontFd = SOCKET_INVALID;
|
||||
int backFd = SOCKET_INVALID;
|
||||
WOLFSSL* front = NULL;
|
||||
WOLFSSL* back = NULL;
|
||||
|
||||
printf("\nWaiting for a client...\n");
|
||||
frontFd = accept(mListenfd, (struct sockaddr*)&clientAddr, &size);
|
||||
if (frontFd == -1) {
|
||||
if (mShutdown)
|
||||
break;
|
||||
fprintf(stderr, "ERROR: failed to accept\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Terminate the legacy client connection. */
|
||||
if ((front = wolfSSL_new(frontCtx)) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create frontend WOLFSSL\n");
|
||||
goto conn_cleanup;
|
||||
}
|
||||
wolfSSL_set_fd(front, frontFd);
|
||||
if (wolfSSL_accept(front) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: frontend handshake failed: %d\n",
|
||||
wolfSSL_get_error(front, 0));
|
||||
goto conn_cleanup;
|
||||
}
|
||||
|
||||
/* Open the quantum-safe connection to the origin. */
|
||||
backFd = connect_backend(backHost, backPort);
|
||||
if (backFd < 0) {
|
||||
fprintf(stderr, "ERROR: failed to reach backend %s:%d\n",
|
||||
backHost, backPort);
|
||||
goto conn_cleanup;
|
||||
}
|
||||
if ((back = wolfSSL_new(backCtx)) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create backend WOLFSSL\n");
|
||||
goto conn_cleanup;
|
||||
}
|
||||
wolfSSL_set_fd(back, backFd);
|
||||
/* Pre-generate the hybrid key share so the very first ClientHello to
|
||||
* the origin already carries the post-quantum group. */
|
||||
if (wolfSSL_UseKeyShare(back, PQC_GROUP) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to set backend key share\n");
|
||||
goto conn_cleanup;
|
||||
}
|
||||
if (wolfSSL_connect(back) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: backend handshake failed: %d\n",
|
||||
wolfSSL_get_error(back, 0));
|
||||
goto conn_cleanup;
|
||||
}
|
||||
|
||||
printf("Bridging connection:\n");
|
||||
print_leg("frontend", front);
|
||||
print_leg("backend", back);
|
||||
|
||||
relay(front, frontFd, back, backFd);
|
||||
printf("Connection closed\n");
|
||||
|
||||
conn_cleanup:
|
||||
if (front) {
|
||||
wolfSSL_shutdown(front);
|
||||
wolfSSL_free(front);
|
||||
}
|
||||
if (back) {
|
||||
wolfSSL_shutdown(back);
|
||||
wolfSSL_free(back);
|
||||
}
|
||||
if (frontFd != SOCKET_INVALID)
|
||||
close(frontFd);
|
||||
if (backFd != SOCKET_INVALID)
|
||||
close(backFd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
int frontPort = UPGRADE_PORT;
|
||||
const char* backHost = PQSERVER_HOST;
|
||||
int backPort = PQSERVER_PORT;
|
||||
int on = 1;
|
||||
|
||||
struct sockaddr_in servAddr;
|
||||
|
||||
/* The backend group is restricted to a single post-quantum (hybrid) group
|
||||
* so that the proxy-to-server leg is always quantum-safe. */
|
||||
int backGroups[] = { PQC_GROUP };
|
||||
|
||||
WOLFSSL_CTX* frontCtx = NULL; /* frontend: legacy TLS 1.2 server */
|
||||
WOLFSSL_CTX* backCtx = NULL; /* backend: PQC TLS 1.3 client */
|
||||
|
||||
if (argc >= 2) frontPort = atoi(argv[1]);
|
||||
if (argc >= 3) backHost = argv[2];
|
||||
if (argc >= 4) backPort = atoi(argv[3]);
|
||||
if (argc > 4) {
|
||||
printf("usage: %s [<frontend port> [<backend host> "
|
||||
"[<backend port>]]]\n", argv[0]);
|
||||
printf("Defaults: %d %s %d\n", UPGRADE_PORT, PQSERVER_HOST,
|
||||
PQSERVER_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
wolfSSL_Init();
|
||||
|
||||
/* ---- Frontend context: legacy classical TLS 1.2 server --------------- */
|
||||
if ((frontCtx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create frontend WOLFSSL_CTX\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_set_cipher_list(frontCtx, LEGACY_CIPHERS))
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to set frontend cipher list\n");
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_use_certificate_file(frontCtx, LEGACY_CERT,
|
||||
WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", LEGACY_CERT);
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_use_PrivateKey_file(frontCtx, LEGACY_KEY,
|
||||
WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", LEGACY_KEY);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* ---- Backend context: post-quantum / hybrid TLS 1.3 client ----------- */
|
||||
if ((backCtx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())) == NULL) {
|
||||
fprintf(stderr, "ERROR: failed to create backend WOLFSSL_CTX\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
if ((ret = wolfSSL_CTX_load_verify_locations(backCtx, PQC_CA, NULL))
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to load %s\n", PQC_CA);
|
||||
goto exit;
|
||||
}
|
||||
/* Only negotiate the hybrid PQC group with the origin. */
|
||||
if ((ret = wolfSSL_CTX_set_groups(backCtx, backGroups,
|
||||
sizeof(backGroups) / sizeof(backGroups[0]))) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to set backend PQC group\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* ---- Listen on the frontend port ------------------------------------- */
|
||||
if ((mListenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to create the socket\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
setsockopt(mListenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
memset(&servAddr, 0, sizeof(servAddr));
|
||||
servAddr.sin_family = AF_INET;
|
||||
servAddr.sin_port = htons(frontPort);
|
||||
servAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(mListenfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to bind\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
if (listen(mListenfd, 5) == -1) {
|
||||
fprintf(stderr, "ERROR: failed to listen\n");
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("PQC upgrade-proxy ready\n");
|
||||
printf(" frontend : 0.0.0.0:%d TLS 1.2 %s\n",
|
||||
frontPort, LEGACY_CIPHERS);
|
||||
printf(" backend : %s:%d TLS 1.3 kex=%s auth=%s\n",
|
||||
backHost, backPort, PQC_GROUP_NAME, PQC_AUTH);
|
||||
|
||||
ret = accept_loop(frontCtx, backCtx, backHost, backPort);
|
||||
|
||||
printf("Shutdown complete\n");
|
||||
|
||||
exit:
|
||||
if (mListenfd != SOCKET_INVALID)
|
||||
close(mListenfd);
|
||||
if (frontCtx)
|
||||
wolfSSL_CTX_free(frontCtx);
|
||||
if (backCtx)
|
||||
wolfSSL_CTX_free(backCtx);
|
||||
wolfSSL_Cleanup();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("This example requires TLS 1.3 and ML-KEM (Kyber).\n");
|
||||
printf("Configure wolfSSL like this:\n");
|
||||
printf(" ./configure --enable-mlkem --enable-tls13\n");
|
||||
printf("Add --enable-mldsa for post-quantum authentication too.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM */
|
||||
Loading…
Reference in New Issue