This guide provides a systematic, step-by-step procedure to set up a complete RISC-V embedded development environment with Post-Quantum Cryptography (ML-KEM-512 and Dilithium/ML-DSA-44) support.
Our specific objective and core implementation for this project was to architect, integrate, and establish a quantum-secure DTLS 1.3 handshake on this constrained embedded system. To achieve this Post-Quantum Cryptography (PQC) security layer, we implemented:
- ML-KEM-512 (FIPS 203, NIST level 1 — the standardised CRYSTALS-Kyber KEM) for secure, quantum-resistant key exchange. The client explicitly forces this group via
wolfSSL_CTX_set_groups(), so the handshake never silently falls back to classical ECDHE. - Dilithium / ML-DSA-44 (FIPS 204, NIST level 2) for quantum-resistant digital signatures and mutual certificate authentication. The CA, server, and client certificates are all genuine ML-DSA certificates.
Ensure your system has the following installed:
- Python 3.8+
- Git
- Build tools:
build-essential,cmake,autoconf,automake,libtool - Linux environment (Ubuntu 20.04 or newer recommended)
This repository contains a complete RISC-V embedded system with Post-Quantum Cryptography support, organized into the following directories:
Bare-metal firmware for the RISC-V embedded client:
main.c- Main client firmware implementing DTLS 1.3 handshake with Dilithium PQC certificatescrt0.d/linker.ld- RISC-V bootloader and memory layout configurationMakefile- Build system for compiling the firmwarewolfssl/- WolfSSL/WolfCrypt headers and certificate datacerts_dilithium_data.h- Auto-generated C arrays containing embedded Dilithium certificates (CA, client cert, client key)
src/- Additional firmware source fileswolfcrypt/- WolfCrypt cryptographic library headers
Host-side server implementations and certificate generation tools:
dtls13_dilithium_server.c- DTLS 1.3 server with Dilithium PQC supportserver- Compiled server binarygenerate_dilithium_certs.sh- Script for generating Dilithium certificatesgenerate_dilithium_certs.c- C implementation for certificate generationinstall_pqc_wolfssl.sh- WolfSSL PQC installation automation scriptcerts_dilithium/- Generated Dilithium PQC certificates (ca-cert.pem, server-cert.pem, client-cert.pem, keys)
Build artifacts and intermediate files:
sim/- LiteX simulation build output (CSR definitions, Verilog, memory maps)
These directories contain the LiteX SoC framework and peripherals:
Main LiteX SoC framework - provides FPGA/simulation infrastructure for RISC-V CPU and peripherals
Board support packages and hardware platform definitions
LiteDRAM controller - DRAM memory controller core
LiteEth - Ethernet MAC and PHY implementation (used for network communication in this project)
Logic analyzer for debugging FPGA designs
SD card controller module
SPI flash controller
SATA controller implementation
PCIe controller core
Inter-chip communication links (SerDes)
JESD204B high-speed serial interface
I2C controller implementation
RISC-V and other CPU implementations in Python HDL format:
VexRiscv - Primary RISC-V CPU core used in this project (32-bit, customizable pipeline)
VexRiscv SMP - Multi-core variant
VexiiRiscv - Next-generation VexRiscv implementation
pythondata-cpu-lm32/- LatticeMico32 soft processorpythondata-cpu-minerva/- Minerva RISC-V corepythondata-cpu-mor1kx/- OpenRISC processorpythondata-cpu-naxriscv/- NaxRiscv RISC-V corepythondata-cpu-sentinel/- Sentinel RISC-V corepythondata-cpu-serv/- SERV bit-serial RISC-V core
Migen - Python-based HDL (Hardware Description Language) toolbox, foundation for LiteX
Compiler runtime support libraries
Picolibc - Embedded C library for bare-metal systems
TAP network interface configuration utilities
USB OHCI controller implementation
USB device controller core
WolfSSL library source code with PQC support (Dilithium, ML-KEM, Kyber)
Python virtual environment containing all LiteX dependencies and tools
Master setup script for initializing LiteX environment and installing toolchains
csr.json- Control/Status Register definitions for the simulated SoCREADME.md- This comprehensive setup guide
┌─────────────────────────────────────────────────────────────┐
│ Certificate Generation │
│ host/generate_dilithium_certs.sh │
│ ↓ │
│ host/certs_dilithium/*.pem │
│ ↓ │
│ boot/wolfssl/certs_dilithium_data.h │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Firmware Build │
│ boot/main.c + boot/wolfssl/certs_dilithium_data.h │
│ ↓ │
│ litex_bare_metal_demo │
│ ↓ │
│ boot.bin (RISC-V firmware with embedded PQC certs) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Runtime Execution │
│ Server: host/server (192.168.1.100:6000) │
│ ↕ DTLS 1.3 + Dilithium (ML-DSA) + ML-KEM-512 │
│ Client: litex_sim + boot.bin (192.168.1.50:60000) │
│ ↑ │
│ VexRiscv CPU @ 1MHz with LiteEth network │
└─────────────────────────────────────────────────────────────┘
Clone the project repository containing the LiteX configuration and simulation modules:
git clone https://github.com/divyansh-1009/Inter_IIT_Cybersecurity_ID-67.git
cd Inter_IIT_Cybersecurity_ID-67Isolate LiteX and Python dependencies using a virtual environment:
python3 -m venv litex-env
source litex-env/bin/activateInstall required system packages:
sudo apt update
sudo apt install -y libevent-dev libjson-c-dev verilator meson ninja-build autoconf automake libtoolMake the setup script executable and initialize:
chmod +x litex_setup.py
./litex_setup.py --init --installInstall additional Python dependencies:
pip3 install meson ninjaInstall the RISC-V GCC toolchain using the LiteX setup utility:
sudo ./litex_setup.py --gcc=riscvClone the WolfSSL repository:
git clone https://github.com/wolfSSL/wolfssl.git
cd wolfsslRun the autoconf setup:
./autogen.shConfigure with comprehensive PQC and DTLS support:
./configure \
--enable-opensslcoexist \
--enable-opensslextra \
--enable-opensslall \
--enable-dilithium \
--enable-mlkem \
--enable-kyber \
--enable-sp \
--enable-debug \
--enable-certgen \
--enable-pkcs7 \
--enable-pkcs12 \
--enable-tlsx \
--enable-dtls \
--enable-dtls13 \
--enable-dtls-frag-ch \
CFLAGS="-DWC_ENABLE_DILITHIUM -DWC_ENABLE_MLKEM -DWOLFSSL_STATIC_RSA -DWOLFSSL_STATIC_DH"Build and install:
make -j$(nproc)
sudo make install
sudo ldconfigReturn to the project directory:
cd ..Generate the Dilithium (ML-DSA-44) CA, server, and client certificates:
./host/generate_dilithium_certs.shThe script uses OpenSSL's native ML-DSA-44 support (requires OpenSSL >= 3.5;
verify with openssl list -signature-algorithms | grep ML-DSA). No liboqs / OQS
provider is needed. It aborts with an error rather than silently falling back to
classical ECC if ML-DSA is unavailable.
This creates the following in host/certs_dilithium/ (PEM + DER, all ML-DSA-44):
- ca-cert.pem / ca-cert.der - Dilithium Root CA certificate
- server-cert.pem / server-key.pem - Dilithium server certificate + private key
- client-cert.pem / client-key.pem - Dilithium client certificate + private key
It also regenerates boot/wolfssl/certs_dilithium_data.h, the embedded C arrays (CA cert, client cert, client key) that the firmware compiles in.
Verify the algorithm at any time with:
openssl x509 -in host/certs_dilithium/ca-cert.pem -noout -text | grep "Signature Algorithm"— this must reportML-DSA-44, notecdsa-with-SHA256.
The embedded header is tracked in the repo:
- boot/wolfssl/certs_dilithium_data.h - C header containing embedded certificate arrays for the firmware
Configure the tap0 virtual network interface for RISC-V simulation to communicate with the host server:
If tap0 already exists and is busy, remove it first:
sudo ip link set tap0 down
sudo ip link del tap0Create and configure tap0:
sudo ip tuntap add dev tap0 mode tap
sudo ip addr flush dev tap0
sudo ip addr add 192.168.1.100/24 dev tap0
sudo ip link set tap0 upVerify the interface:
ip addr show tap0You should see output similar to:
3: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
link/ether 12:34:56:78:9a:bc brd ff:ff:ff:ff:ff:ff
inet 192.168.1.100/24 scope global tap0
Compile the server binary with WolfSSL support:
gcc host/dtls13_dilithium_server.c -o host/server \
-I/usr/local/include \
-L/usr/local/lib \
-Wl,-rpath=/usr/local/lib \
-lwolfsslVerify the server binary was created:
ls -la host/serverTerminal 1 - Start the DTLS Server:
./host/serverThe server will:
- Listen on
192.168.1.100:6000 - Load the Dilithium CA certificate to verify client authenticity
- Load the Dilithium server certificate and private key
- Require mutual TLS authentication with PQC certificates
- Use DTLS 1.3 with AES-128-GCM-SHA256 cipher suite
- Advertise ML-KEM-512 (plus ML-KEM hybrids) for post-quantum key exchange
Expected output:
Starting Dilithium PQC DTLS Server...
Listening on 192.168.1.100:6000
Waiting for client connections...
Terminal 2 - In a new terminal, ensure virtual environment is active:
source litex-env/bin/activateRegenerate the bare-metal demo firmware (now with embedded Dilithium certificates):
litex_bare_metal_demo --build-path=build/simThis creates:
- boot.bin - Compiled RISC-V firmware with embedded Dilithium certificates
Terminal 2 - Launch the RISC-V simulation client:
litex_sim --csr-json csr.json \
--cpu-type=vexriscv \
--cpu-variant=full \
--integrated-main-ram-size=0x06400000 \
--ram-init=boot.bin \
--with-ethernetThe simulation will:
- Start a RISC-V VexRiscv CPU running at 1MHz
- Load the compiled firmware (
boot.bin) into simulated RAM - Initialize network interface at
192.168.1.50:60000 - Perform DTLS 1.3 handshake with server using Dilithium certificates
- Validate server's certificate against Dilithium CA
- Present client certificate for mutual authentication
- Establish encrypted channel with post-quantum cryptography
- Server Ready - Waiting for client connection on
192.168.1.100:6000 - Client Boot - RISC-V loads firmware from boot.bin
- Network Initialize - Embedded system obtains IP
192.168.1.50(simulated) - DTLS Initiation - Client initiates handshake with server
- Certificate Exchange - Both parties exchange and validate Dilithium certificates
- Handshake Completion - Mutual authentication established (takes 30-60 seconds due to 1MHz CPU)
- Encrypted Communication - Application data exchanged over secured channel
- Loads Dilithium (ML-DSA-44) CA certificate (~4.1 KB) from embedded arrays
- Loads Dilithium client certificate (~4.1 KB) and private key (~2.6 KB)
- Forces the ML-KEM-512 key-exchange group via
wolfSSL_CTX_set_groups() - Initiates DTLS 1.3 handshake with server
- Validates server's Dilithium certificate against CA
- Presents client Dilithium certificate for mutual authentication
- Uses TLS13-AES128-GCM-SHA256 cipher
- Performs Post-Quantum Key Exchange (ML-KEM-512)
- Sends encrypted application data
- Loads Dilithium CA certificate to verify client
- Loads server certificate and private key
- Validates client Dilithium certificate against CA
- Completes DTLS 1.3 handshake with PQC support
- Receives and processes encrypted data
- Demonstrates quantum-resistant mutual authentication
In a third terminal, capture traffic on the tap0 interface:
sudo tcpdump -i tap0 -nn udp and port 6000Monitor client output in Terminal 2 (simulation) and server output in Terminal 1 to observe the handshake progress.
Verify tap0 is active during the simulation:
ip addr show tap0| Issue | Solution |
|---|---|
| "No such file or directory" (certificates) | Ensure Dilithium certificates are generated: ./host/generate_dilithium_certs.sh |
| "Device or resource busy" (tap0) | Remove and recreate tap0: sudo ip link del tap0 then repeat Step 9 |
| "Command not found" (litex_sim) | Activate virtual environment: source litex-env/bin/activate |
| Server compilation fails | Verify wolfSSL installed: ldconfig -p | grep wolfssl |
| Handshake timeout | Normal behavior with 1MHz simulated CPU - wait 30-60 seconds |
| Client won't connect | Ensure tap0 is up and server is running on correct IP (192.168.1.100:6000) |
| Handshake picks classical ECDHE | Confirm the client's wolfSSL_CTX_set_groups(ctx, {WOLFSSL_ML_KEM_512}, 1) call succeeded and the server advertises ML-KEM (both require WOLFSSL_HAVE_MLKEM) |
| Certificates decode as ECDSA | Regenerate with ./host/generate_dilithium_certs.sh; the CA cert must report Signature Algorithm: ML-DSA-44 |
Server fails to load its ML-DSA key (NOT_COMPILED_IN / make_key_from_seed) |
The key was written in seed+expanded form. Regenerate with ./host/generate_dilithium_certs.sh — it emits priv-only (expanded) keys via OpenSSL's -provparam ml-dsa.output_formats=priv-only, which load on both wolfCrypt and liboqs builds. Requires OpenSSL >= 3.5. |
The following choices target the evaluation metrics (latency, throughput, CPU cycles, memory) on the 1 MHz simulated RISC-V core:
- PQC KEM = ML-KEM-512 (pure). The lowest-cost standardised KEM (FIPS 203,
NIST level 1), minimising key-exchange cycles and handshake bytes versus
higher levels or hybrid groups. Forced explicitly with
wolfSSL_CTX_set_groups(). - Speed-tuned wolfCrypt build. The SoC has ~100 MiB of RAM, so the
WOLFSSL_*_SMALL_MEM,WOLFSSL_DILITHIUM_SMALL,WOLFSSL_DILITHIUM_NO_LARGE_CODEandWOLFSSL_SHA3_SMALLpaths are not enabled — they trade speed for a RAM saving this platform does not need. This is the single largest handshake-latency lever. (Expect a higher heap/ROM figure in exchange.) - No verbose TLS trace on the hot path.
wolfSSL_Debugging_ON()is not called andDEBUG_WOLFSSL*are off; the internal trace was emitted over the blocking UART inside the timed handshake at 1 MHz. - Expanded (
priv-only) ML-DSA keys. Avoids a runtime seed→key expansion on the client and is the format wolfSSL loads directly (see troubleshooting above). - Fuller RNG utilisation. The custom PRNG emits all four bytes of each 32-bit xorshift word (previously one), cutting RNG work on the ML-KEM keygen path ~4x.
These are reproducible knobs, not measured results. Regenerate
evidence/on the Linux build host (seeevidence/README.md) to capture the actual latency/throughput/footprint after building with this configuration.
host/generate_dilithium_certs.sh- Certificate generation script with Dilithium naminghost/dtls13_dilithium_server.c- Server implementation with Dilithium PQC supportboot/main.c- Client firmware with embedded Dilithium certificatesboot/wolfssl/certs_dilithium_data.h- Embedded Dilithium certificate arrays (auto-generated)
source litex-env/bin/activatecd Constraint_Env_Sim
source litex-env/bin/activate
./host/generate_dilithium_certs.sh
sudo ip tuntap add dev tap0 mode tap
sudo ip addr add 192.168.1.100/24 dev tap0
sudo ip link set tap0 up# Terminal 1: Start server
./host/server
# Terminal 2: Build and run client
source litex-env/bin/activate
litex_bare_metal_demo --build-path=build/sim
litex_sim --csr-json csr.json --cpu-type=vexriscv --cpu-variant=full \
--integrated-main-ram-size=0x06400000 --ram-init=boot.bin --with-ethernet
# Terminal 3: Monitor traffic (optional)
sudo tcpdump -i tap0 -nn udp and port 6000✅ Repository cloning and environment setup
✅ WolfSSL with comprehensive PQC support (Dilithium, ML-KEM, Kyber)
✅ Dilithium certificate generation and conversion
✅ tap0 network interface configuration
✅ DTLS 1.3 server with PQC authentication
✅ Embedded client firmware with quantum-resistant certificates
✅ Mutual TLS authentication with CA validation
✅ Post-quantum key exchange (ML-KEM-512)
- WolfSSL Documentation: https://github.com/wolfSSL/wolfssl
- LiteX Documentation: https://github.com/enjoy-digital/litex
- Post-Quantum Cryptography: https://en.wikipedia.org/wiki/Post-quantum_cryptography
- DTLS 1.3 RFC: https://tools.ietf.org/html/rfc9147
Last Updated: December 2025
Version: 1.1
Status: Demonstration / proof-of-concept (simulation on LiteX + Verilator)