Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/pqc-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,88 @@ jobs:
config.log
run.out
retention-days: 5

# Full TPM-backed PQC TLS 1.3 handshake: ML-KEM key exchange + ML-DSA
# CertificateVerify signed on the TPM (device key never leaves the chip).
pqc-tls-examples:
name: PQC TLS 1.3 examples (ML-DSA auth + ML-KEM)
permissions:
contents: read
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
container:
image: ghcr.io/wolfssl/wolftpm-ci:v1.0
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
timeout-minutes: 30

steps:
- name: Checkout wolfTPM
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Setup wolfSSL with PQC TLS
uses: ./.github/actions/setup-wolfssl
with:
configure-flags: >-
--enable-wolftpm --enable-pkcallbacks --enable-keygen
--enable-certgen --enable-dilithium --enable-mlkem
--enable-experimental --enable-tls-mlkem-standalone --enable-harden
cflags: -DWOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ -DWC_RSA_NO_PADDING
prefix: $HOME/wolfssl-install

- name: wolfSSL version info
run: grep LIBWOLFSSL_VERSION_STRING "$HOME/wolfssl-install/include/wolfssl/version.h"

- name: Build wolfTPM with v1.85 + fwTPM + debug
run: |
./autogen.sh
CPPFLAGS="-I$HOME/wolfssl-install/include" \
LDFLAGS="-L$HOME/wolfssl-install/lib -Wl,-rpath,$HOME/wolfssl-install/lib" \
./configure --enable-v185 --enable-fwtpm --enable-debug=verbose
make -j"$(nproc)"

# A stub means wolfSSL was missing a required feature (cert-gen, ML-DSA
# keygen/sign, private-key-id, or TLS 1.3) — fail loudly rather than skip.
- name: Verify PQC TLS examples built (not stubs)
run: |
for b in examples/pqc/gen_pqc_certs \
examples/tls/tls_server examples/tls/tls_client; do
if ./$b -h 2>&1 | grep -q "Requires"; then
echo "FAIL: $b is a stub — wolfSSL missing a required feature"
exit 1
fi
done

- name: PQC TLS handshake E2E (ML-KEM x ML-DSA matrix)
run: |
set +e
export LD_LIBRARY_PATH="$HOME/wolfssl-install/lib"
# run_examples.sh pairs the classical TLS tests with the wolfSSL
# example client/server, so point it at the clone built above
export WOLFSSL_PATH="$GITHUB_WORKSPACE/wolfssl"
./src/fwtpm/fwtpm_server >/tmp/fwtpm_tls.log 2>&1 &
echo $! > /tmp/fwtpm_tls.pid
sleep 2
ENABLE_PQC_TLS=1 ./examples/run_examples.sh
rc=$?
exit $rc

- name: Stop fwtpm_server (PQC TLS)
if: always()
run: kill "$(cat /tmp/fwtpm_tls.pid)" 2>/dev/null || true

- name: Upload failure logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: pqc-tls-examples-logs
path: |
/tmp/fwtpm_tls.log
run.out
pqtls.out
config.log
tests/*.log
retention-days: 5
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ examples/keygen/ecdh
examples/pqc/mldsa_sign
examples/pqc/mlkem_encap
examples/pqc/pqc_mssim_e2e
examples/pqc/gen_pqc_certs
examples/nvram/extend
examples/nvram/store
examples/nvram/read
Expand Down Expand Up @@ -118,6 +119,7 @@ certs/server-*.der
certs/server-*.pem
certs/client-*.der
certs/client-*.pem
certs/pq-*.der
certs/serial.old
certs/0*.pem
certs/1*.pem
Expand Down
66 changes: 65 additions & 1 deletion examples/pqc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,20 @@ the full fwTPM PQC reference.

```
./configure --enable-wolftpm --enable-mldsa --enable-mlkem \
--enable-harden --enable-keygen
--enable-tls-mlkem-standalone --enable-experimental \
--enable-harden --enable-keygen --enable-certgen
make
sudo make install
```

`--enable-tls-mlkem-standalone` is required for the standalone `ML_KEM_*` TLS
groups; without it wolfSSL only offers the hybrid groups and
`wolfSSL_UseKeyShare` rejects the client default.

`--enable-certgen` is needed by the TLS `gen_pqc_certs` tool below;
`--enable-wolftpm` provides the crypto callback and private-key-id support the
TLS server uses.

**wolfTPM**:

```
Expand Down Expand Up @@ -172,3 +181,58 @@ restricted key with no symmetric algorithm via `TPM_RC_SYMMETRIC`).
./examples/keygen/create_primary -mldsa # default MLDSA-65
./examples/keygen/create_primary -mldsa=87 -oh
```

## Post-Quantum TLS 1.3 (ML-KEM + TPM ML-DSA)

A full TLS 1.3 handshake where the server's ML-DSA identity key lives in the
TPM. The server signs the CertificateVerify on-chip via the wolfTPM crypto
callback; the client performs an ML-KEM key exchange and validates the server
against a software CA.

Requires a wolfSSL that routes `wc_MlDsaKey_SignCtx` to the crypto callback for
device keys (private key in the TPM). That landed upstream, so master or any
later release works. No shipping TPM implements TCG v1.85 PQC yet, so this runs
against the in-tree fwTPM.

Demo scope: the identity key is an unauthenticated deterministic TPM primary
(empty auth), reproducible by both `gen_pqc_certs` and the server from the owner
hierarchy. A production deployment should protect the identity key with a
non-empty auth value or policy so it cannot be recreated from the public cert.
The client validates the server chain against the demo CA but does not bind the
certificate to the host name, so the demo connects to the default localhost and
does not pass `-h=`. Supplying `-h=` turns on strict verification including
`wolfSSL_check_domain_name`, which this leaf cannot satisfy; a production
deployment should issue the leaf with a matching subjectAltName.

Three programs:
- `examples/pqc/gen_pqc_certs` — makes a software ML-DSA CA and a device leaf
cert whose subject key is the TPM ML-DSA key.
- `examples/tls/tls_server -mldsa` — recreates that TPM key and serves TLS 1.3.
- `examples/tls/tls_client -mldsa` — connects, ML-KEM key exchange, verifies the CA.

```
./src/fwtpm/fwtpm_server --clear &

# 1. certificate chain bound to the TPM key (-mldsa must match the server)
./examples/pqc/gen_pqc_certs -mldsa=65

# 2. server (same -mldsa as gen_pqc_certs)
./examples/tls/tls_server -p=11111 -mldsa=65 &

# 3. client (choose the ML-KEM group)
./examples/tls/tls_client -p=11111 -mldsa -group=ML_KEM_768
```

Options:
- `gen_pqc_certs -mldsa=44/65/87` — ML-DSA parameter set.
- `tls_server -p=<port> -mldsa=44/65/87`.
- `tls_client -h=<host> -p=<port> -group=<name>` where `<name>` is
`ML_KEM_512/768/1024` or a hybrid `SECP256R1MLKEM768` / `X25519MLKEM768`
(hybrids need the matching classical curve enabled in wolfSSL).

The one-shot end-to-end test drives all three and asserts the ML-KEM group,
TPM-signed ML-DSA authentication, CA verification, and app data:

```
ENABLE_PQC_TLS=1 ./examples/run_examples.sh # includes the PQC TLS matrix
```
Loading
Loading