mirror of https://github.com/wolfSSL/wolfTPM.git
Add TPM backed ML-DSA authentication for TLS 1.3 with PQC example and tests
parent
7d02b58e4c
commit
0063c16e7e
|
|
@ -172,3 +172,87 @@ 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)
|
||||
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
|
||||
|
||||
# TODO(revert-when-merged): build wolfSSL from the aidangarske/wolfssl
|
||||
# mldsa-signctx-cryptocb-devkey branch because the wc_MlDsaKey_SignCtx
|
||||
# crypto-callback fix (device-key ML-DSA signing) is not yet upstream.
|
||||
# Once that wolfSSL PR merges, switch this back to a wolfSSL/wolfssl tag
|
||||
# (>= the release carrying the fix) and delete this note.
|
||||
- name: Build wolfSSL (PQC TLS + crypto-cb ML-DSA device-key fix)
|
||||
run: |
|
||||
cd ~
|
||||
git clone --depth 1 \
|
||||
--branch mldsa-signctx-cryptocb-devkey \
|
||||
https://github.com/aidangarske/wolfssl.git
|
||||
cd wolfssl
|
||||
./autogen.sh
|
||||
./configure --enable-wolftpm --enable-pkcallbacks --enable-keygen \
|
||||
--enable-certgen --enable-dilithium --enable-mlkem \
|
||||
--enable-experimental --enable-tls-mlkem-standalone --enable-harden \
|
||||
CPPFLAGS="-DWOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ -DWC_RSA_NO_PADDING" \
|
||||
--prefix="$HOME/wolfssl-install"
|
||||
make -j"$(nproc)"
|
||||
make 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_pq examples/tls/tls_client_pq; 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: |
|
||||
export LD_LIBRARY_PATH="$HOME/wolfssl-install/lib"
|
||||
rc=0
|
||||
for combo in "ML_KEM_768 65" "ML_KEM_512 44" \
|
||||
"ML_KEM_1024 87" "SECP256R1MLKEM768 65"; do
|
||||
set -- $combo
|
||||
echo "== group $1 / ML-DSA-$2 =="
|
||||
if ! ./tests/tls_pq_e2e.sh "$1" "$2"; then rc=1; break; fi
|
||||
done
|
||||
exit $rc
|
||||
|
||||
- name: Upload failure logs
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pqc-tls-examples-logs
|
||||
path: |
|
||||
/tmp/tls_pq_e2e_*.log
|
||||
config.log
|
||||
tests/*.log
|
||||
retention-days: 5
|
||||
|
|
|
|||
|
|
@ -64,11 +64,14 @@ pkcs7tpmsigned.p7s
|
|||
pkcs7tpmsignedex.p7s
|
||||
examples/tls/tls_server
|
||||
examples/tls/tls_client_notpm
|
||||
examples/tls/tls_client_pq
|
||||
examples/tls/tls_server_pq
|
||||
tests/unit.test
|
||||
tests/unit.log
|
||||
tests/fwtpm_unit.log
|
||||
tests/fwtpm_unit.test
|
||||
tests/fwtpm_check.sh.log
|
||||
tests/tls_pq_e2e.sh.log
|
||||
examples/keygen/create_primary
|
||||
examples/keygen/keyload
|
||||
examples/keygen/keygen
|
||||
|
|
@ -78,6 +81,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
|
||||
|
|
@ -118,6 +122,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
|
||||
|
|
|
|||
|
|
@ -14,11 +14,15 @@ the full fwTPM PQC reference.
|
|||
|
||||
```
|
||||
./configure --enable-wolftpm --enable-mldsa --enable-mlkem \
|
||||
--enable-harden --enable-keygen
|
||||
--enable-harden --enable-keygen --enable-certgen
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
`--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**:
|
||||
|
||||
```
|
||||
|
|
@ -172,3 +176,56 @@ 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 wolfSSL with a fix that routes `wc_MlDsaKey_SignCtx` to the crypto
|
||||
callback for device keys (private key in the TPM). 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; a production client should also issue the leaf with
|
||||
a matching subjectAltName and call `wolfSSL_check_domain_name` before connecting.
|
||||
|
||||
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_pq` — recreates that TPM key and serves TLS 1.3.
|
||||
- `examples/tls/tls_client_pq` — 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_pq -p=11111 -mldsa=65 &
|
||||
|
||||
# 3. client (choose the ML-KEM group)
|
||||
./examples/tls/tls_client_pq -h=localhost -p=11111 -group=ML_KEM_768
|
||||
```
|
||||
|
||||
Options:
|
||||
- `gen_pqc_certs -mldsa=44/65/87` — ML-DSA parameter set.
|
||||
- `tls_server_pq -p=<port> -mldsa=44/65/87`.
|
||||
- `tls_client_pq -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:
|
||||
|
||||
```
|
||||
./tests/tls_pq_e2e.sh # ML_KEM_768 + ML-DSA-65
|
||||
./tests/tls_pq_e2e.sh SECP256R1MLKEM768 87
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,299 @@
|
|||
/* gen_pqc_certs.c
|
||||
*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfTPM.
|
||||
*
|
||||
* wolfTPM 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfTPM 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-1335, USA
|
||||
*/
|
||||
|
||||
/* Generate a software ML-DSA CA and a device leaf certificate whose subject key
|
||||
* is a TPM-resident ML-DSA key, for the TLS PQC examples. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <wolftpm/tpm2_wrap.h>
|
||||
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
||||
!defined(NO_FILESYSTEM) && \
|
||||
defined(WOLFTPM_MLDSA) && defined(WOLFSSL_CERT_GEN) && \
|
||||
!defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \
|
||||
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
|
||||
!defined(WOLFSSL_MLDSA_NO_SIGN) && !defined(WOLFSSL_DILITHIUM_NO_SIGN) && \
|
||||
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_DILITHIUM_NO_ASN1)
|
||||
|
||||
#include <hal/tpm_io.h>
|
||||
#include <examples/tpm_test.h>
|
||||
|
||||
#include <wolfssl/wolfcrypt/wc_mldsa.h>
|
||||
#include <wolfssl/wolfcrypt/asn_public.h>
|
||||
#include <wolfssl/wolfcrypt/random.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CA_CERT_FILE "./certs/pq-ca-cert.der"
|
||||
#define SERVER_CERT_FILE "./certs/pq-server-cert.der"
|
||||
|
||||
/* generous for ML-DSA-87 cert (pub ~2592 + sig ~4627 + overhead) */
|
||||
#define PQC_CERT_BUF_SZ 10000
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Expected usage:\n");
|
||||
printf("./examples/pqc/gen_pqc_certs [-mldsa=44/65/87]\n");
|
||||
printf("* -mldsa=44/65/87: ML-DSA parameter set (default 65)\n");
|
||||
printf("Outputs %s and %s\n", CA_CERT_FILE, SERVER_CERT_FILE);
|
||||
}
|
||||
|
||||
static int parseParamSet(const char* v, TPMI_MLDSA_PARAMETER_SET* ps)
|
||||
{
|
||||
if (XSTRCMP(v, "44") == 0) { *ps = TPM_MLDSA_44; return 0; }
|
||||
if (XSTRCMP(v, "65") == 0) { *ps = TPM_MLDSA_65; return 0; }
|
||||
if (XSTRCMP(v, "87") == 0) { *ps = TPM_MLDSA_87; return 0; }
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
static int mldsaTypes(TPMI_MLDSA_PARAMETER_SET ps, int* keyType, int* sigType,
|
||||
int* wcLevel)
|
||||
{
|
||||
switch (ps) {
|
||||
case TPM_MLDSA_44:
|
||||
*keyType = ML_DSA_44_TYPE; *sigType = CTC_ML_DSA_44;
|
||||
*wcLevel = WC_ML_DSA_44; return 0;
|
||||
case TPM_MLDSA_65:
|
||||
*keyType = ML_DSA_65_TYPE; *sigType = CTC_ML_DSA_65;
|
||||
*wcLevel = WC_ML_DSA_65; return 0;
|
||||
case TPM_MLDSA_87:
|
||||
*keyType = ML_DSA_87_TYPE; *sigType = CTC_ML_DSA_87;
|
||||
*wcLevel = WC_ML_DSA_87; return 0;
|
||||
default:
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
static int writeDer(const char* file, const byte* der, int derSz)
|
||||
{
|
||||
XFILE f = XFOPEN(file, "wb");
|
||||
if (f == XBADFILE) {
|
||||
printf("Failed to open %s for write\n", file);
|
||||
return -1;
|
||||
}
|
||||
if (XFWRITE(der, 1, (size_t)derSz, f) != (size_t)derSz) {
|
||||
XFCLOSE(f);
|
||||
return -1;
|
||||
}
|
||||
XFCLOSE(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int TPM2_PQC_GenCerts(void* userCtx, int argc, char *argv[])
|
||||
{
|
||||
int rc = 0, sz = 0;
|
||||
WOLFTPM2_DEV dev;
|
||||
WOLFTPM2_KEY mldsaKey;
|
||||
TPMT_PUBLIC pub;
|
||||
TPMI_MLDSA_PARAMETER_SET paramSet = TPM_MLDSA_65;
|
||||
int keyType = ML_DSA_65_TYPE, sigType = CTC_ML_DSA_65;
|
||||
int wcLevel = WC_ML_DSA_65;
|
||||
WC_RNG rng;
|
||||
wc_MlDsaKey caKey;
|
||||
wc_MlDsaKey tpmPubKey;
|
||||
Cert caCert;
|
||||
Cert leafCert;
|
||||
byte* caDer = NULL;
|
||||
byte* leafDer = NULL;
|
||||
int caSz = 0, leafSz = 0;
|
||||
int haveRng = 0, haveCaKey = 0, havePubKey = 0;
|
||||
|
||||
XMEMSET(&dev, 0, sizeof(dev));
|
||||
XMEMSET(&mldsaKey, 0, sizeof(mldsaKey));
|
||||
XMEMSET(&pub, 0, sizeof(pub));
|
||||
XMEMSET(&caKey, 0, sizeof(caKey));
|
||||
XMEMSET(&tpmPubKey, 0, sizeof(tpmPubKey));
|
||||
XMEMSET(&caCert, 0, sizeof(caCert));
|
||||
XMEMSET(&leafCert, 0, sizeof(leafCert));
|
||||
|
||||
if (argc >= 2 && (XSTRCMP(argv[1], "-?") == 0 ||
|
||||
XSTRCMP(argv[1], "-h") == 0 || XSTRCMP(argv[1], "--help") == 0)) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
while (argc > 1) {
|
||||
if (XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) {
|
||||
if (parseParamSet(argv[argc-1] + 7, ¶mSet) != 0) {
|
||||
usage();
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
argc--;
|
||||
}
|
||||
(void)mldsaTypes(paramSet, &keyType, &sigType, &wcLevel);
|
||||
|
||||
caDer = (byte*)XMALLOC(PQC_CERT_BUF_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
leafDer = (byte*)XMALLOC(PQC_CERT_BUF_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (caDer == NULL || leafDer == NULL) {
|
||||
rc = MEMORY_E;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
rc = wolfTPM2_Init(&dev, TPM2_IoCb, userCtx);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("wolfTPM2_Init failed 0x%x: %s\n", rc,
|
||||
wolfTPM2_GetRCString(rc));
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
/* TPM ML-DSA device key (deterministic; the server recreates it) */
|
||||
rc = wolfTPM2_GetKeyTemplate_MLDSA(&pub,
|
||||
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
||||
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
||||
TPMA_OBJECT_noDA, paramSet, 0);
|
||||
if (rc == TPM_RC_SUCCESS) {
|
||||
rc = wolfTPM2_CreatePrimaryKey(&dev, &mldsaKey, TPM_RH_OWNER, &pub,
|
||||
NULL, 0);
|
||||
}
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("Create TPM ML-DSA key failed 0x%x: %s\n",
|
||||
rc, wolfTPM2_GetRCString(rc));
|
||||
}
|
||||
else {
|
||||
printf("TPM ML-DSA device key: handle 0x%08x, pub %u bytes\n",
|
||||
(unsigned)mldsaKey.handle.hndl,
|
||||
(unsigned)mldsaKey.pub.publicArea.unique.mldsa.size);
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
rc = wc_InitRng(&rng);
|
||||
if (rc == 0) haveRng = 1;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
/* software CA key */
|
||||
rc = wc_MlDsaKey_Init(&caKey, NULL, INVALID_DEVID);
|
||||
if (rc == 0) haveCaKey = 1;
|
||||
if (rc == 0) rc = wc_MlDsaKey_SetParams(&caKey, wcLevel);
|
||||
if (rc == 0) rc = wc_MlDsaKey_MakeKey(&caKey, &rng);
|
||||
if (rc != 0) printf("CA key gen failed %d\n", rc);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
/* self-signed CA cert */
|
||||
rc = wc_InitCert(&caCert);
|
||||
}
|
||||
if (rc == 0) {
|
||||
caCert.daysValid = 365;
|
||||
caCert.selfSigned = 1;
|
||||
caCert.isCA = 1;
|
||||
caCert.sigType = sigType;
|
||||
XSTRNCPY(caCert.subject.country, "US", CTC_NAME_SIZE);
|
||||
XSTRNCPY(caCert.subject.org, "wolfSSL", CTC_NAME_SIZE);
|
||||
XSTRNCPY(caCert.subject.commonName, "wolfTPM PQC Demo CA",
|
||||
CTC_NAME_SIZE);
|
||||
XMEMCPY(&caCert.issuer, &caCert.subject, sizeof(CertName));
|
||||
sz = wc_MakeCert_ex(&caCert, caDer, PQC_CERT_BUF_SZ, keyType, &caKey,
|
||||
&rng);
|
||||
if (sz < 0) rc = sz;
|
||||
}
|
||||
if (rc == 0) {
|
||||
sz = wc_SignCert_ex(caCert.bodySz, caCert.sigType, caDer,
|
||||
PQC_CERT_BUF_SZ, keyType, &caKey, &rng);
|
||||
if (sz < 0) rc = sz;
|
||||
else caSz = sz;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
/* TPM public key for the leaf subject */
|
||||
rc = wc_MlDsaKey_Init(&tpmPubKey, NULL, INVALID_DEVID);
|
||||
if (rc == 0) havePubKey = 1;
|
||||
if (rc == 0) rc = wc_MlDsaKey_SetParams(&tpmPubKey, wcLevel);
|
||||
if (rc == 0) rc = wc_MlDsaKey_ImportPubRaw(&tpmPubKey,
|
||||
mldsaKey.pub.publicArea.unique.mldsa.buffer,
|
||||
mldsaKey.pub.publicArea.unique.mldsa.size);
|
||||
if (rc != 0) printf("import TPM pub failed %d\n", rc);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
/* device leaf cert: subject = TPM key, issuer = CA, CA-signed */
|
||||
rc = wc_InitCert(&leafCert);
|
||||
}
|
||||
if (rc == 0) {
|
||||
leafCert.daysValid = 365;
|
||||
leafCert.isCA = 0;
|
||||
leafCert.sigType = sigType;
|
||||
XSTRNCPY(leafCert.subject.country, "US", CTC_NAME_SIZE);
|
||||
XSTRNCPY(leafCert.subject.org, "wolfSSL", CTC_NAME_SIZE);
|
||||
XSTRNCPY(leafCert.subject.commonName, "wolfTPM ML-DSA Device",
|
||||
CTC_NAME_SIZE);
|
||||
rc = wc_SetIssuerBuffer(&leafCert, caDer, caSz);
|
||||
}
|
||||
if (rc == 0) {
|
||||
sz = wc_MakeCert_ex(&leafCert, leafDer, PQC_CERT_BUF_SZ, keyType,
|
||||
&tpmPubKey, &rng);
|
||||
if (sz < 0) rc = sz;
|
||||
}
|
||||
if (rc == 0) {
|
||||
sz = wc_SignCert_ex(leafCert.bodySz, leafCert.sigType, leafDer,
|
||||
PQC_CERT_BUF_SZ, keyType, &caKey, &rng);
|
||||
if (sz < 0) rc = sz;
|
||||
else leafSz = sz;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
rc = writeDer(CA_CERT_FILE, caDer, caSz);
|
||||
if (rc == 0) rc = writeDer(SERVER_CERT_FILE, leafDer, leafSz);
|
||||
if (rc == 0) {
|
||||
printf("Wrote %s (%d bytes) and %s (%d bytes)\n",
|
||||
CA_CERT_FILE, caSz, SERVER_CERT_FILE, leafSz);
|
||||
}
|
||||
}
|
||||
|
||||
if (havePubKey) wc_MlDsaKey_Free(&tpmPubKey);
|
||||
if (haveCaKey) wc_MlDsaKey_Free(&caKey);
|
||||
if (haveRng) wc_FreeRng(&rng);
|
||||
if (mldsaKey.handle.hndl != 0)
|
||||
wolfTPM2_UnloadHandle(&dev, &mldsaKey.handle);
|
||||
wolfTPM2_Cleanup(&dev);
|
||||
XFREE(caDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(leafDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* !WOLFTPM2_NO_WRAPPER && !WOLFTPM2_NO_WOLFCRYPT && WOLFTPM_MLDSA */
|
||||
|
||||
#ifndef NO_MAIN_DRIVER
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc = -1;
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
||||
!defined(NO_FILESYSTEM) && \
|
||||
defined(WOLFTPM_MLDSA) && defined(WOLFSSL_CERT_GEN) && \
|
||||
!defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \
|
||||
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
|
||||
!defined(WOLFSSL_MLDSA_NO_SIGN) && !defined(WOLFSSL_DILITHIUM_NO_SIGN) && \
|
||||
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_DILITHIUM_NO_ASN1)
|
||||
rc = TPM2_PQC_GenCerts(NULL, argc, argv);
|
||||
#else
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
printf("Requires --enable-pqc (v1.85 ML-DSA) and wolfSSL cert gen\n");
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -29,6 +29,11 @@ examples_pqc_mlkem_decap_neg_SOURCES = examples/pqc/mlkem_decap_neg.c
|
|||
examples_pqc_mlkem_decap_neg_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD)
|
||||
examples_pqc_mlkem_decap_neg_DEPENDENCIES = src/libwolftpm.la
|
||||
|
||||
noinst_PROGRAMS += examples/pqc/gen_pqc_certs
|
||||
examples_pqc_gen_pqc_certs_SOURCES = examples/pqc/gen_pqc_certs.c
|
||||
examples_pqc_gen_pqc_certs_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD)
|
||||
examples_pqc_gen_pqc_certs_DEPENDENCIES = src/libwolftpm.la
|
||||
|
||||
EXTRA_DIST += examples/pqc/README.md
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -26,12 +26,30 @@ examples_tls_tls_server_SOURCES = examples/tls/tls_server.c \
|
|||
examples_tls_tls_server_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD)
|
||||
examples_tls_tls_server_DEPENDENCIES = src/libwolftpm.la
|
||||
|
||||
if BUILD_PQC
|
||||
noinst_PROGRAMS += examples/tls/tls_client_pq
|
||||
noinst_HEADERS += examples/tls/tls_client_pq.h
|
||||
examples_tls_tls_client_pq_SOURCES = examples/tls/tls_client_pq.c
|
||||
examples_tls_tls_client_pq_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD)
|
||||
examples_tls_tls_client_pq_DEPENDENCIES = src/libwolftpm.la
|
||||
|
||||
noinst_PROGRAMS += examples/tls/tls_server_pq
|
||||
noinst_HEADERS += examples/tls/tls_server_pq.h
|
||||
examples_tls_tls_server_pq_SOURCES = examples/tls/tls_server_pq.c
|
||||
examples_tls_tls_server_pq_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD)
|
||||
examples_tls_tls_server_pq_DEPENDENCIES = src/libwolftpm.la
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
example_tlsdir = $(exampledir)/tls
|
||||
dist_example_tls_DATA = \
|
||||
examples/tls/tls_client.c \
|
||||
examples/tls/tls_server.c
|
||||
examples/tls/tls_server.c \
|
||||
examples/tls/tls_client_pq.c \
|
||||
examples/tls/tls_server_pq.c
|
||||
|
||||
DISTCLEANFILES+= examples/tls/.libs/tls_client
|
||||
DISTCLEANFILES+= examples/tls/.libs/tls_server
|
||||
DISTCLEANFILES+= examples/tls/.libs/tls_client_pq
|
||||
DISTCLEANFILES+= examples/tls/.libs/tls_server_pq
|
||||
|
|
|
|||
|
|
@ -0,0 +1,242 @@
|
|||
/* tls_client_pq.c
|
||||
*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfTPM.
|
||||
*
|
||||
* wolfTPM 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfTPM 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-1335, USA
|
||||
*/
|
||||
|
||||
/* TLS 1.3 client for the PQC example: ML-KEM key exchange, validates the
|
||||
* server's TPM ML-DSA certificate against the CA. Pair with tls_server_pq. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <wolftpm/tpm2_wrap.h>
|
||||
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \
|
||||
!defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_WOLFSSL_CLIENT) && \
|
||||
!defined(WOLFCRYPT_ONLY) && defined(WOLFTPM_MLDSA_SIGN) && \
|
||||
defined(WOLFSSL_TLS13) && !defined(NO_FILESYSTEM)
|
||||
|
||||
#ifndef _WIN32
|
||||
/* host-resolve declarations tls_common.h needs on POSIX */
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <examples/tls/tls_client_pq.h>
|
||||
#include <examples/tls/tls_common.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PQ_CA_CERT "./certs/pq-ca-cert.der"
|
||||
#define PQ_CERT_BUF_SZ 10000
|
||||
|
||||
static const char kHelloMsg[] = "hello wolfssl!\n";
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Expected usage:\n");
|
||||
printf("./examples/tls/tls_client_pq [-h=host] [-p=port] [-group=name]\n");
|
||||
printf("* -group: ML_KEM_512/768/1024, or hybrid SECP256R1MLKEM768 /\n");
|
||||
printf(" X25519MLKEM768 (default ML_KEM_768)\n");
|
||||
}
|
||||
|
||||
static int parseGroup(const char* v, int* group)
|
||||
{
|
||||
if (XSTRCMP(v, "ML_KEM_512") == 0) { *group = WOLFSSL_ML_KEM_512; return 0; }
|
||||
if (XSTRCMP(v, "ML_KEM_768") == 0) { *group = WOLFSSL_ML_KEM_768; return 0; }
|
||||
if (XSTRCMP(v, "ML_KEM_1024") == 0) {
|
||||
*group = WOLFSSL_ML_KEM_1024; return 0;
|
||||
}
|
||||
if (XSTRCMP(v, "SECP256R1MLKEM768") == 0) {
|
||||
*group = WOLFSSL_SECP256R1MLKEM768; return 0;
|
||||
}
|
||||
if (XSTRCMP(v, "X25519MLKEM768") == 0) {
|
||||
*group = WOLFSSL_X25519MLKEM768; return 0;
|
||||
}
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
static int readDer(const char* file, byte* der, int* derSz)
|
||||
{
|
||||
XFILE f = XFOPEN(file, "rb");
|
||||
long sz;
|
||||
if (f == XBADFILE) {
|
||||
printf("Cannot open %s (run gen_pqc_certs first)\n", file);
|
||||
return -1;
|
||||
}
|
||||
XFSEEK(f, 0, XSEEK_END); sz = XFTELL(f); XREWIND(f);
|
||||
if (sz <= 0 || sz > *derSz) { XFCLOSE(f); return -1; }
|
||||
if (XFREAD(der, 1, (size_t)sz, f) != (size_t)sz) { XFCLOSE(f); return -1; }
|
||||
XFCLOSE(f);
|
||||
*derSz = (int)sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TPM2_TLS_ClientPQArgs(void* userCtx, int argc, char *argv[])
|
||||
{
|
||||
int rc = 0;
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
WOLFSSL* ssl = NULL;
|
||||
SockIoCbCtx sockIoCtx;
|
||||
const char* host = TLS_HOST;
|
||||
word32 port = TLS_PORT;
|
||||
int group = WOLFSSL_ML_KEM_768;
|
||||
byte* caDer = NULL;
|
||||
int caSz = PQ_CERT_BUF_SZ;
|
||||
char reply[MAX_MSG_SZ];
|
||||
int replySz;
|
||||
|
||||
(void)userCtx;
|
||||
XMEMSET(&sockIoCtx, 0, sizeof(sockIoCtx));
|
||||
sockIoCtx.fd = -1;
|
||||
sockIoCtx.listenFd = -1;
|
||||
|
||||
while (argc > 1) {
|
||||
if (XSTRNCMP(argv[argc-1], "-h=", 3) == 0) {
|
||||
host = argv[argc-1] + 3;
|
||||
}
|
||||
else if (XSTRNCMP(argv[argc-1], "-p=", 3) == 0) {
|
||||
port = (word32)XATOI(argv[argc-1] + 3);
|
||||
}
|
||||
else if (XSTRNCMP(argv[argc-1], "-group=", 7) == 0) {
|
||||
if (parseGroup(argv[argc-1] + 7, &group) != 0) {
|
||||
usage();
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
else if (XSTRCMP(argv[argc-1], "-?") == 0 ||
|
||||
XSTRCMP(argv[argc-1], "-h") == 0) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
argc--;
|
||||
}
|
||||
|
||||
caDer = (byte*)XMALLOC(PQ_CERT_BUF_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (caDer == NULL) {
|
||||
return MEMORY_E;
|
||||
}
|
||||
|
||||
ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
|
||||
if (ctx == NULL) {
|
||||
rc = MEMORY_E;
|
||||
}
|
||||
if (rc == 0) {
|
||||
wolfSSL_CTX_SetIORecv(ctx, SockIORecv);
|
||||
wolfSSL_CTX_SetIOSend(ctx, SockIOSend);
|
||||
/* require the server chain to validate against the software CA */
|
||||
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
|
||||
rc = readDer(PQ_CA_CERT, caDer, &caSz);
|
||||
}
|
||||
if (rc == 0) {
|
||||
if (wolfSSL_CTX_load_verify_buffer(ctx, caDer, caSz,
|
||||
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
|
||||
printf("load_verify_buffer failed\n");
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
ssl = wolfSSL_new(ctx);
|
||||
if (ssl == NULL) {
|
||||
rc = MEMORY_E;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
wolfSSL_SetIOReadCtx(ssl, &sockIoCtx);
|
||||
wolfSSL_SetIOWriteCtx(ssl, &sockIoCtx);
|
||||
if (wolfSSL_UseKeyShare(ssl, (word16)group) != WOLFSSL_SUCCESS) {
|
||||
printf("UseKeyShare failed\n");
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
/* advertise only the chosen group; no silent downgrade */
|
||||
if (wolfSSL_set_groups(ssl, &group, 1) != WOLFSSL_SUCCESS) {
|
||||
printf("set_groups failed\n");
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = SetupSocketAndConnect(&sockIoCtx, host, port);
|
||||
}
|
||||
if (rc == 0) {
|
||||
do {
|
||||
rc = wolfSSL_connect(ssl);
|
||||
} while (rc != WOLFSSL_SUCCESS &&
|
||||
(wolfSSL_want_read(ssl) || wolfSSL_want_write(ssl)));
|
||||
if (rc != WOLFSSL_SUCCESS) {
|
||||
printf("connect failed: %s\n",
|
||||
wolfSSL_ERR_reason_error_string(wolfSSL_get_error(ssl, rc)));
|
||||
}
|
||||
else {
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
printf("Handshake: %s, group %s\n",
|
||||
wolfSSL_get_cipher(ssl), wolfSSL_get_curve_name(ssl));
|
||||
printf("Server ML-DSA identity verified against the CA\n");
|
||||
if (wolfSSL_write(ssl, kHelloMsg, (int)XSTRLEN(kHelloMsg)) <= 0) {
|
||||
printf("write failed: %s\n",
|
||||
wolfSSL_ERR_reason_error_string(wolfSSL_get_error(ssl, 0)));
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
replySz = wolfSSL_read(ssl, reply, sizeof(reply) - 1);
|
||||
if (replySz <= 0) {
|
||||
printf("read failed: %s\n",
|
||||
wolfSSL_ERR_reason_error_string(
|
||||
wolfSSL_get_error(ssl, replySz)));
|
||||
rc = -1;
|
||||
}
|
||||
else {
|
||||
reply[replySz] = 0;
|
||||
printf("Server: %s", reply);
|
||||
}
|
||||
}
|
||||
|
||||
if (ssl != NULL) { wolfSSL_shutdown(ssl); wolfSSL_free(ssl); }
|
||||
if (ctx != NULL) wolfSSL_CTX_free(ctx);
|
||||
CloseAndCleanupSocket(&sockIoCtx);
|
||||
XFREE(caDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef NO_MAIN_DRIVER
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc = -1;
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \
|
||||
!defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_WOLFSSL_CLIENT) && \
|
||||
!defined(WOLFCRYPT_ONLY) && defined(WOLFTPM_MLDSA_SIGN) && \
|
||||
defined(WOLFSSL_TLS13) && !defined(NO_FILESYSTEM)
|
||||
rc = TPM2_TLS_ClientPQArgs(NULL, argc, argv);
|
||||
#else
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
printf("Requires wolfTPM --enable-pqc and wolfSSL crypto callbacks\n");
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/* tls_client_pq.h
|
||||
*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfTPM.
|
||||
*
|
||||
* wolfTPM 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfTPM 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-1335, USA
|
||||
*/
|
||||
|
||||
#ifndef _TPM_TLS_CLIENT_PQ_H_
|
||||
#define _TPM_TLS_CLIENT_PQ_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int TPM2_TLS_ClientPQArgs(void* userCtx, int argc, char *argv[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* _TPM_TLS_CLIENT_PQ_H_ */
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
/* tls_server_pq.c
|
||||
*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfTPM.
|
||||
*
|
||||
* wolfTPM 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfTPM 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-1335, USA
|
||||
*/
|
||||
|
||||
/* TLS 1.3 server whose ML-DSA identity key lives in the TPM. The TPM signs the
|
||||
* CertificateVerify via the wolfTPM crypto callback. Pair with tls_client_pq. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <wolftpm/tpm2_wrap.h>
|
||||
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \
|
||||
!defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_WOLFSSL_SERVER) && \
|
||||
!defined(WOLFCRYPT_ONLY) && defined(WOLFTPM_MLDSA_SIGN) && \
|
||||
defined(WOLF_PRIVATE_KEY_ID) && defined(WOLFSSL_TLS13) && \
|
||||
!defined(NO_FILESYSTEM)
|
||||
|
||||
#ifndef _WIN32
|
||||
/* host-resolve declarations tls_common.h needs on POSIX */
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <examples/tls/tls_server_pq.h>
|
||||
#include <examples/tls/tls_common.h>
|
||||
#include <hal/tpm_io.h>
|
||||
#include <examples/tpm_test.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PQ_SERVER_CERT "./certs/pq-server-cert.der"
|
||||
#define PQ_CERT_BUF_SZ 10000
|
||||
|
||||
static const char kReplyMsg[] = "I hear you fa shizzle!\n";
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Expected usage:\n");
|
||||
printf("./examples/tls/tls_server_pq [-p=port] [-mldsa=44/65/87]\n");
|
||||
printf("* -mldsa=44/65/87: ML-DSA set (must match gen_pqc_certs, def 65)\n");
|
||||
}
|
||||
|
||||
static int parseParamSet(const char* v, TPMI_MLDSA_PARAMETER_SET* ps)
|
||||
{
|
||||
if (XSTRCMP(v, "44") == 0) { *ps = TPM_MLDSA_44; return 0; }
|
||||
if (XSTRCMP(v, "65") == 0) { *ps = TPM_MLDSA_65; return 0; }
|
||||
if (XSTRCMP(v, "87") == 0) { *ps = TPM_MLDSA_87; return 0; }
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
static int readDer(const char* file, byte* der, int* derSz)
|
||||
{
|
||||
XFILE f = XFOPEN(file, "rb");
|
||||
long sz;
|
||||
if (f == XBADFILE) {
|
||||
printf("Cannot open %s (run gen_pqc_certs first)\n", file);
|
||||
return -1;
|
||||
}
|
||||
XFSEEK(f, 0, XSEEK_END); sz = XFTELL(f); XREWIND(f);
|
||||
if (sz <= 0 || sz > *derSz) { XFCLOSE(f); return -1; }
|
||||
if (XFREAD(der, 1, (size_t)sz, f) != (size_t)sz) { XFCLOSE(f); return -1; }
|
||||
XFCLOSE(f);
|
||||
*derSz = (int)sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TPM2_TLS_ServerPQArgs(void* userCtx, int argc, char *argv[])
|
||||
{
|
||||
int rc;
|
||||
WOLFTPM2_DEV dev;
|
||||
WOLFTPM2_KEY mldsaKey;
|
||||
TPMT_PUBLIC pub;
|
||||
TpmCryptoDevCtx tpmCtx;
|
||||
int tpmDevId = INVALID_DEVID;
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
WOLFSSL* ssl = NULL;
|
||||
SockIoCbCtx sockIoCtx;
|
||||
word32 port = TLS_PORT;
|
||||
TPMI_MLDSA_PARAMETER_SET paramSet = TPM_MLDSA_65;
|
||||
const byte keyId[] = { 't','p','m','m','l','d','s','a' };
|
||||
byte* certDer = NULL;
|
||||
int certSz = PQ_CERT_BUF_SZ;
|
||||
char msg[MAX_MSG_SZ];
|
||||
int msgSz;
|
||||
|
||||
XMEMSET(&dev, 0, sizeof(dev));
|
||||
XMEMSET(&mldsaKey, 0, sizeof(mldsaKey));
|
||||
XMEMSET(&pub, 0, sizeof(pub));
|
||||
XMEMSET(&tpmCtx, 0, sizeof(tpmCtx));
|
||||
XMEMSET(&sockIoCtx, 0, sizeof(sockIoCtx));
|
||||
sockIoCtx.fd = -1;
|
||||
sockIoCtx.listenFd = -1;
|
||||
|
||||
while (argc > 1) {
|
||||
if (XSTRCMP(argv[argc-1], "-?") == 0 ||
|
||||
XSTRCMP(argv[argc-1], "-h") == 0) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
else if (XSTRNCMP(argv[argc-1], "-p=", 3) == 0) {
|
||||
port = (word32)XATOI(argv[argc-1] + 3);
|
||||
}
|
||||
else if (XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) {
|
||||
if (parseParamSet(argv[argc-1] + 7, ¶mSet) != 0) {
|
||||
usage();
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
argc--;
|
||||
}
|
||||
|
||||
certDer = (byte*)XMALLOC(PQ_CERT_BUF_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (certDer == NULL) {
|
||||
return MEMORY_E;
|
||||
}
|
||||
|
||||
rc = wolfTPM2_Init(&dev, TPM2_IoCb, userCtx);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("wolfTPM2_Init failed 0x%x: %s\n", rc, wolfTPM2_GetRCString(rc));
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
/* recreate the same TPM ML-DSA identity key gen_pqc_certs certified */
|
||||
rc = wolfTPM2_GetKeyTemplate_MLDSA(&pub,
|
||||
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
||||
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
||||
TPMA_OBJECT_noDA, paramSet, 0);
|
||||
if (rc == TPM_RC_SUCCESS) {
|
||||
rc = wolfTPM2_CreatePrimaryKey(&dev, &mldsaKey, TPM_RH_OWNER, &pub,
|
||||
NULL, 0);
|
||||
}
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("Create TPM ML-DSA key failed 0x%x: %s\n",
|
||||
rc, wolfTPM2_GetRCString(rc));
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
tpmCtx.dev = &dev;
|
||||
tpmCtx.mldsaKey = &mldsaKey;
|
||||
rc = wolfTPM2_SetCryptoDevCb(&dev, wolfTPM2_CryptoDevCb, &tpmCtx,
|
||||
&tpmDevId);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("SetCryptoDevCb failed 0x%x\n", rc);
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
|
||||
if (ctx == NULL) {
|
||||
rc = MEMORY_E;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
wolfSSL_CTX_SetDevId(ctx, tpmDevId);
|
||||
wolfSSL_CTX_SetIORecv(ctx, SockIORecv);
|
||||
wolfSSL_CTX_SetIOSend(ctx, SockIOSend);
|
||||
rc = readDer(PQ_SERVER_CERT, certDer, &certSz);
|
||||
}
|
||||
if (rc == 0) {
|
||||
if (wolfSSL_CTX_use_certificate_buffer(ctx, certDer, certSz,
|
||||
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
|
||||
printf("use_certificate_buffer failed\n");
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
/* private key stays in the TPM: reference it by id + devId */
|
||||
if (wolfSSL_CTX_use_PrivateKey_Id(ctx, keyId, sizeof(keyId), tpmDevId)
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
printf("use_PrivateKey_Id failed\n");
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
ssl = wolfSSL_new(ctx);
|
||||
if (ssl == NULL) {
|
||||
rc = MEMORY_E;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
wolfSSL_SetIOReadCtx(ssl, &sockIoCtx);
|
||||
wolfSSL_SetIOWriteCtx(ssl, &sockIoCtx);
|
||||
rc = SetupSocketAndListen(&sockIoCtx, port);
|
||||
}
|
||||
if (rc == 0) {
|
||||
printf("PQC TLS server listening on port %u\n", (unsigned)port);
|
||||
rc = SocketWaitClient(&sockIoCtx);
|
||||
}
|
||||
if (rc == 0) {
|
||||
do {
|
||||
rc = wolfSSL_accept(ssl);
|
||||
} while (rc != WOLFSSL_SUCCESS &&
|
||||
(wolfSSL_want_read(ssl) || wolfSSL_want_write(ssl)));
|
||||
if (rc != WOLFSSL_SUCCESS) {
|
||||
printf("accept failed: %s\n",
|
||||
wolfSSL_ERR_reason_error_string(wolfSSL_get_error(ssl, rc)));
|
||||
}
|
||||
else {
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
printf("Handshake: %s, group %s (ML-DSA identity signed on TPM)\n",
|
||||
wolfSSL_get_cipher(ssl), wolfSSL_get_curve_name(ssl));
|
||||
msgSz = wolfSSL_read(ssl, msg, sizeof(msg) - 1);
|
||||
if (msgSz <= 0) {
|
||||
printf("read failed: %s\n",
|
||||
wolfSSL_ERR_reason_error_string(wolfSSL_get_error(ssl, msgSz)));
|
||||
rc = -1;
|
||||
}
|
||||
else {
|
||||
msg[msgSz] = 0;
|
||||
printf("Client: %s", msg);
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
if (wolfSSL_write(ssl, kReplyMsg, (int)XSTRLEN(kReplyMsg)) <= 0) {
|
||||
printf("write failed: %s\n",
|
||||
wolfSSL_ERR_reason_error_string(wolfSSL_get_error(ssl, 0)));
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssl != NULL) { wolfSSL_shutdown(ssl); wolfSSL_free(ssl); }
|
||||
if (ctx != NULL) wolfSSL_CTX_free(ctx);
|
||||
CloseAndCleanupSocket(&sockIoCtx);
|
||||
if (mldsaKey.handle.hndl != 0)
|
||||
wolfTPM2_UnloadHandle(&dev, &mldsaKey.handle);
|
||||
wolfTPM2_Cleanup(&dev);
|
||||
XFREE(certDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef NO_MAIN_DRIVER
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc = -1;
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \
|
||||
!defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_WOLFSSL_SERVER) && \
|
||||
!defined(WOLFCRYPT_ONLY) && defined(WOLFTPM_MLDSA_SIGN) && \
|
||||
defined(WOLF_PRIVATE_KEY_ID) && defined(WOLFSSL_TLS13) && \
|
||||
!defined(NO_FILESYSTEM)
|
||||
rc = TPM2_TLS_ServerPQArgs(NULL, argc, argv);
|
||||
#else
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
printf("Requires wolfTPM --enable-pqc and wolfSSL crypto callbacks\n");
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/* tls_server_pq.h
|
||||
*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfTPM.
|
||||
*
|
||||
* wolfTPM 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfTPM 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-1335, USA
|
||||
*/
|
||||
|
||||
#ifndef _TPM_TLS_SERVER_PQ_H_
|
||||
#define _TPM_TLS_SERVER_PQ_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int TPM2_TLS_ServerPQArgs(void* userCtx, int argc, char *argv[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* _TPM_TLS_SERVER_PQ_H_ */
|
||||
|
|
@ -82,7 +82,7 @@ int wolfTPM2_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx)
|
|||
rc = wolfTPM2_GetRandom(tlsCtx->dev, info->seed.seed, info->seed.sz);
|
||||
#endif /* !WC_NO_RNG */
|
||||
}
|
||||
#if !defined(NO_RSA) || defined(HAVE_ECC)
|
||||
#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(WOLFTPM_MLDSA_SIGN)
|
||||
else if (info->algo_type == WC_ALGO_TYPE_PK) {
|
||||
#ifndef NO_RSA
|
||||
/* RSA */
|
||||
|
|
@ -425,8 +425,51 @@ int wolfTPM2_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx)
|
|||
#endif /* !WOLFTPM2_USE_SW_ECDHE */
|
||||
}
|
||||
#endif /* HAVE_ECC */
|
||||
#ifdef WOLFTPM_MLDSA_SIGN
|
||||
if (info->pk.type == WC_PK_TYPE_PQC_SIG_SIGN) {
|
||||
TPM_HANDLE seqHandle = 0;
|
||||
WOLFTPM2_HANDLE seqHandleObj;
|
||||
int sigSz;
|
||||
|
||||
/* pure ML-DSA one-shot only; pre-hash, oversized, no-key, bad-arg
|
||||
* fall back */
|
||||
if (info->pk.pqc_sign.type != WC_PQC_SIG_TYPE_MLDSA ||
|
||||
info->pk.pqc_sign.preHashType != WC_HASH_TYPE_NONE ||
|
||||
info->pk.pqc_sign.out == NULL ||
|
||||
info->pk.pqc_sign.outlen == NULL ||
|
||||
(info->pk.pqc_sign.in == NULL &&
|
||||
info->pk.pqc_sign.inlen > 0) ||
|
||||
info->pk.pqc_sign.inlen > MAX_DIGEST_BUFFER ||
|
||||
tlsCtx->mldsaKey == NULL) {
|
||||
return exit_rc;
|
||||
}
|
||||
sigSz = (int)*info->pk.pqc_sign.outlen;
|
||||
|
||||
rc = wolfTPM2_SignSequenceStart(tlsCtx->dev, tlsCtx->mldsaKey,
|
||||
info->pk.pqc_sign.context, (int)info->pk.pqc_sign.contextLen,
|
||||
&seqHandle);
|
||||
if (rc == 0) {
|
||||
rc = wolfTPM2_SignSequenceComplete(tlsCtx->dev, seqHandle,
|
||||
tlsCtx->mldsaKey, info->pk.pqc_sign.in,
|
||||
(int)info->pk.pqc_sign.inlen, info->pk.pqc_sign.out, &sigSz);
|
||||
if (rc == 0) {
|
||||
*info->pk.pqc_sign.outlen = (word32)sigSz;
|
||||
}
|
||||
}
|
||||
if (rc != 0 && seqHandle != 0) {
|
||||
/* free seq on failure */
|
||||
XMEMSET(&seqHandleObj, 0, sizeof(seqHandleObj));
|
||||
seqHandleObj.hndl = seqHandle;
|
||||
wolfTPM2_UnloadHandle(tlsCtx->dev, &seqHandleObj);
|
||||
}
|
||||
if (rc == BUFFER_E) {
|
||||
/* preserve caller size-error; don't mask it as WC_HW_E */
|
||||
return BUFFER_E;
|
||||
}
|
||||
}
|
||||
#endif /* WOLFTPM_MLDSA_SIGN */
|
||||
}
|
||||
#endif /* !NO_RSA || HAVE_ECC */
|
||||
#endif /* !NO_RSA || HAVE_ECC || WOLFTPM_MLDSA_SIGN */
|
||||
#ifndef NO_AES
|
||||
else if (info->algo_type == WC_ALGO_TYPE_CIPHER) {
|
||||
if (info->cipher.type != WC_CIPHER_AES_CBC) {
|
||||
|
|
|
|||
|
|
@ -41,3 +41,10 @@ if BUILD_FWTPM
|
|||
dist_noinst_SCRIPTS += tests/fwtpm_check.sh \
|
||||
tests/fwtpm_da_retry.sh
|
||||
endif
|
||||
|
||||
# tls_pq_e2e.sh is intentionally NOT a `make check` test: it needs the wolfSSL
|
||||
# crypto-cb ML-DSA device-key fix (not yet upstream) and a cert-gen build, and
|
||||
# it manages its own fwTPM server + NV file (which would collide with the
|
||||
# parallel fwtpm_check.sh). It is distributed and run standalone by the
|
||||
# pqc-examples CI workflow instead.
|
||||
EXTRA_DIST += tests/tls_pq_e2e.sh
|
||||
|
|
|
|||
|
|
@ -0,0 +1,221 @@
|
|||
#!/bin/bash
|
||||
# tls_pq_e2e.sh
|
||||
#
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfTPM.
|
||||
#
|
||||
# wolfTPM 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 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# wolfTPM 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-1335, USA
|
||||
#
|
||||
|
||||
# End-to-end harness for the post-quantum TLS example: spawn fwtpm_server,
|
||||
# generate a TPM-keyed ML-DSA cert chain, run tls_server_pq and tls_client_pq
|
||||
# over a real TCP socket, and assert the full PQC handshake (ML-KEM key
|
||||
# exchange + TPM-signed ML-DSA authentication, CA-verified) plus app data.
|
||||
|
||||
set -u
|
||||
|
||||
# returns: 0 = in use, 1 = free, 2 = no probe tool
|
||||
check_port_in_use() {
|
||||
if command -v nc >/dev/null 2>&1; then
|
||||
nc -z localhost "$1" 2>/dev/null; return $?
|
||||
elif command -v ss >/dev/null 2>&1; then
|
||||
ss -tln 2>/dev/null | grep -qE "[:.]$1 "; return $?
|
||||
elif command -v netstat >/dev/null 2>&1; then
|
||||
netstat -an 2>/dev/null | grep -qE "[:.]$1 .*LISTEN"; return $?
|
||||
fi
|
||||
return 2
|
||||
}
|
||||
|
||||
pick_available_port() {
|
||||
local port attempts=0
|
||||
while [ $attempts -lt 20 ]; do
|
||||
if command -v shuf >/dev/null 2>&1; then port=$(shuf -i 10000-60000 -n 1)
|
||||
else port=$(( (RANDOM % 50000) + 10000 )); fi
|
||||
check_port_in_use "$port"; [ $? -ne 0 ] && { echo "$port"; return 0; }
|
||||
attempts=$((attempts + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# pick a port whose consecutive platform port (port+1) is also free
|
||||
pick_available_pair() {
|
||||
local port attempts=0
|
||||
while [ $attempts -lt 20 ]; do
|
||||
port=$(pick_available_port) || return 1
|
||||
check_port_in_use "$((port + 1))" || { echo "$port"; return 0; }
|
||||
attempts=$((attempts + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# wait for $1 to reach LISTEN (ss/netstat, not nc -z which eats the accept slot)
|
||||
wait_for_listen() {
|
||||
local port="$1" pid="$2" elapsed=0
|
||||
if ! command -v ss >/dev/null 2>&1 && ! command -v netstat >/dev/null 2>&1; then
|
||||
sleep 2; return 0
|
||||
fi
|
||||
while [ $elapsed -lt 400 ]; do
|
||||
kill -0 "$pid" 2>/dev/null || return 1
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss -tln 2>/dev/null | grep -qE "[:.]${port} " && return 0
|
||||
elif netstat -an 2>/dev/null | grep -qE "[:.]${port} .*LISTEN"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.02
|
||||
elapsed=$((elapsed + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# binaries and generated artifacts live in the build tree (VPATH-safe)
|
||||
BUILD_DIR="$(pwd)"
|
||||
cd "$BUILD_DIR" || exit 1
|
||||
|
||||
SERVER="$BUILD_DIR/src/fwtpm/fwtpm_server"
|
||||
GEN="$BUILD_DIR/examples/pqc/gen_pqc_certs"
|
||||
TLS_SRV="$BUILD_DIR/examples/tls/tls_server_pq"
|
||||
TLS_CLI="$BUILD_DIR/examples/tls/tls_client_pq"
|
||||
# fwTPM uses the compile-time FWTPM_NV_FILE (cwd-relative), not an env var
|
||||
NV_FILE="$BUILD_DIR/fwtpm_nv.bin"
|
||||
LOGP="/tmp/tls_pq_e2e_$$"
|
||||
# examples honor TPM2_SWTPM_PORT only when built with getenv support
|
||||
HAS_GETENV=1
|
||||
WOLFTPM_OPTS="$BUILD_DIR/wolftpm/options.h"
|
||||
if [ -f "$WOLFTPM_OPTS" ] && grep -q "^#define NO_GETENV" "$WOLFTPM_OPTS"; then
|
||||
HAS_GETENV=0
|
||||
fi
|
||||
PORT="${TPM2_SWTPM_PORT:-2321}"
|
||||
if check_port_in_use "$PORT" || check_port_in_use "$((PORT + 1))"; then
|
||||
if [ "$HAS_GETENV" -eq 1 ]; then
|
||||
PORT="$(pick_available_pair)"
|
||||
else
|
||||
echo "SKIP: TPM ports $PORT/$((PORT + 1)) busy (NO_GETENV build)" >&2
|
||||
exit 77
|
||||
fi
|
||||
fi
|
||||
PLATFORM_PORT=$((PORT + 1))
|
||||
TLS_PORT="$(pick_available_port)"
|
||||
port_tries=0
|
||||
while { [ "$TLS_PORT" = "$PORT" ] || [ "$TLS_PORT" = "$PLATFORM_PORT" ]; } \
|
||||
&& [ $port_tries -lt 10 ]; do
|
||||
TLS_PORT="$(pick_available_port)"
|
||||
port_tries=$((port_tries + 1))
|
||||
done
|
||||
GROUP="${1:-ML_KEM_768}"
|
||||
MLDSA="${2:-65}"
|
||||
if [ -z "$PORT" ] || [ -z "$TLS_PORT" ]; then
|
||||
echo "SKIP: could not allocate a free port" >&2; exit 77
|
||||
fi
|
||||
|
||||
for bin in "$SERVER" "$GEN" "$TLS_SRV" "$TLS_CLI"; do
|
||||
if [ ! -x "$bin" ]; then
|
||||
echo "SKIP: $(basename "$bin") not built — configure --enable-fwtpm --enable-swtpm --enable-pqc" >&2
|
||||
exit 77
|
||||
fi
|
||||
done
|
||||
|
||||
# the examples compile executable stubs when wolfSSL cert-gen / private-key-id /
|
||||
# TLS 1.3 support is missing; skip rather than fail in that build matrix
|
||||
for bin in "$GEN" "$TLS_SRV" "$TLS_CLI"; do
|
||||
if "$bin" -h 2>&1 | grep -q "Requires"; then
|
||||
echo "SKIP: $(basename "$bin") built without required wolfSSL features" >&2
|
||||
exit 77
|
||||
fi
|
||||
done
|
||||
|
||||
export TPM2_SWTPM_PORT=$PORT
|
||||
|
||||
rm -f "$NV_FILE"
|
||||
|
||||
echo "== Starting fwtpm_server on port $PORT =="
|
||||
"$SERVER" \
|
||||
--port $PORT --platform-port $PLATFORM_PORT --clear \
|
||||
>"${LOGP}_fwtpm.log" 2>&1 &
|
||||
SERVER_PID=$!
|
||||
|
||||
(
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
if exec 3<>/dev/tcp/127.0.0.1/$PORT; then exec 3>&-; exit 0; fi
|
||||
sleep 0.5
|
||||
done
|
||||
exit 1
|
||||
) 2>/dev/null
|
||||
if [ $? -ne 0 ] || ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
||||
echo "FAIL: fwtpm_server not accepting connections on port $PORT" >&2
|
||||
kill "$SERVER_PID" 2>/dev/null; rm -f "$NV_FILE"; exit 1
|
||||
fi
|
||||
|
||||
TLS_SRV_PID=""
|
||||
cleanup() {
|
||||
[ -n "$TLS_SRV_PID" ] && kill "$TLS_SRV_PID" 2>/dev/null
|
||||
TLS_SRV_PID=""
|
||||
[ -n "$SERVER_PID" ] && { kill "$SERVER_PID" 2>/dev/null; \
|
||||
wait "$SERVER_PID" 2>/dev/null; }
|
||||
SERVER_PID=""
|
||||
rm -f "$NV_FILE" "$BUILD_DIR/certs/pq-ca-cert.der" \
|
||||
"$BUILD_DIR/certs/pq-server-cert.der"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
mkdir -p "$BUILD_DIR/certs"
|
||||
echo "== Generating TPM ML-DSA-$MLDSA cert chain =="
|
||||
if ! "$GEN" -mldsa=$MLDSA >"${LOGP}_gen.log" 2>&1; then
|
||||
echo "FAIL: gen_pqc_certs" >&2; cat "${LOGP}_gen.log" >&2; cleanup; exit 1
|
||||
fi
|
||||
|
||||
echo "== Starting tls_server_pq on port $TLS_PORT =="
|
||||
"$TLS_SRV" -p=$TLS_PORT -mldsa=$MLDSA >"${LOGP}_server.log" 2>&1 &
|
||||
TLS_SRV_PID=$!
|
||||
if ! wait_for_listen "$TLS_PORT" "$TLS_SRV_PID"; then
|
||||
echo "FAIL: tls_server_pq not listening on port $TLS_PORT" >&2
|
||||
cat "${LOGP}_server.log" >&2; cleanup; exit 1
|
||||
fi
|
||||
|
||||
echo "== Running tls_client_pq (group $GROUP) =="
|
||||
"$TLS_CLI" -h=localhost -p=$TLS_PORT -group=$GROUP >"${LOGP}_client.log" 2>&1
|
||||
CLI_RC=$?
|
||||
|
||||
# if the client connected, the server serves it and exits; wait to flush its
|
||||
# log. Otherwise (e.g. unsupported group) kill it so we do not hang forever.
|
||||
if [ $CLI_RC -eq 0 ]; then
|
||||
wait "$TLS_SRV_PID" 2>/dev/null
|
||||
else
|
||||
kill "$TLS_SRV_PID" 2>/dev/null
|
||||
fi
|
||||
TLS_SRV_PID=""
|
||||
|
||||
cleanup
|
||||
|
||||
CLI=$(cat "${LOGP}_client.log")
|
||||
SRV=$(cat "${LOGP}_server.log")
|
||||
FAIL=0
|
||||
echo "$CLI" | grep -qi "group $GROUP" || { echo "FAIL: client did not negotiate $GROUP" >&2; FAIL=1; }
|
||||
echo "$CLI" | grep -q "verified against the CA" || { echo "FAIL: client did not verify server cert" >&2; FAIL=1; }
|
||||
echo "$CLI" | grep -q "I hear you fa shizzle" || { echo "FAIL: no app data from server" >&2; FAIL=1; }
|
||||
echo "$SRV" | grep -q "signed on TPM" || { echo "FAIL: server did not sign on the TPM" >&2; FAIL=1; }
|
||||
|
||||
if [ $CLI_RC -eq 0 ] && [ $FAIL -eq 0 ]; then
|
||||
echo "OK: PQC TLS handshake (ML-KEM $GROUP + TPM ML-DSA-$MLDSA) passed"
|
||||
exit 0
|
||||
fi
|
||||
echo "FAIL: PQC TLS E2E (client rc=$CLI_RC)" >&2
|
||||
if ! echo "$SRV" | grep -q "signed on TPM"; then
|
||||
echo "HINT: server never signed on the TPM — the linked wolfSSL may lack the" >&2
|
||||
echo " wc_MlDsaKey_SignCtx crypto-callback route (see examples/pqc/README.md)" >&2
|
||||
fi
|
||||
echo "--- client ---" >&2; echo "$CLI" >&2
|
||||
echo "--- server ---" >&2; echo "$SRV" >&2
|
||||
exit 1
|
||||
|
|
@ -32,6 +32,9 @@
|
|||
#include <wolftpm/tpm2_swtpm.h>
|
||||
#include <wolftpm/tpm2_tis.h>
|
||||
#include <wolftpm/tpm2_spdm.h>
|
||||
#ifdef WOLFTPM_MLDSA_SIGN
|
||||
#include <wolfssl/wolfcrypt/wc_mldsa.h>
|
||||
#endif
|
||||
|
||||
#include <hal/tpm_io.h>
|
||||
#include <examples/tpm_test.h>
|
||||
|
|
@ -4667,6 +4670,160 @@ static void test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void test_wolfTPM2_CryptoDevCb_MlDsaSign(void)
|
||||
{
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \
|
||||
!defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM_MLDSA_SIGN)
|
||||
int rc;
|
||||
WOLFTPM2_DEV dev;
|
||||
WOLFTPM2_KEY dummyKey;
|
||||
WOLFTPM2_KEY tpmKey;
|
||||
TPMT_PUBLIC pub;
|
||||
TpmCryptoDevCtx tpmCtx;
|
||||
wc_CryptoInfo info;
|
||||
byte msg[32];
|
||||
byte sig[64];
|
||||
byte signContext[4];
|
||||
word32 sigLen = (word32)sizeof(sig);
|
||||
byte* bigSig = NULL;
|
||||
word32 bigSigLen;
|
||||
#ifdef WOLFTPM_MLDSA_VERIFY
|
||||
byte otherContext[4];
|
||||
TPM_HANDLE vSeq;
|
||||
TPMT_TK_VERIFIED vtk;
|
||||
#endif
|
||||
|
||||
XMEMSET(&tpmCtx, 0, sizeof(tpmCtx));
|
||||
XMEMSET(&dummyKey, 0, sizeof(dummyKey));
|
||||
XMEMSET(&tpmKey, 0, sizeof(tpmKey));
|
||||
XMEMSET(&pub, 0, sizeof(pub));
|
||||
XMEMSET(msg, 0x5A, sizeof(msg));
|
||||
|
||||
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
||||
AssertIntEQ(rc, 0);
|
||||
tpmCtx.dev = &dev;
|
||||
|
||||
XMEMSET(&info, 0, sizeof(info));
|
||||
info.algo_type = WC_ALGO_TYPE_PK;
|
||||
info.pk.type = WC_PK_TYPE_PQC_SIG_SIGN;
|
||||
info.pk.pqc_sign.type = WC_PQC_SIG_TYPE_MLDSA;
|
||||
info.pk.pqc_sign.in = msg;
|
||||
info.pk.pqc_sign.inlen = (word32)sizeof(msg);
|
||||
info.pk.pqc_sign.out = sig;
|
||||
info.pk.pqc_sign.outlen = &sigLen;
|
||||
|
||||
/* no key: fall back */
|
||||
tpmCtx.mldsaKey = NULL;
|
||||
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
||||
AssertIntEQ(rc, CRYPTOCB_UNAVAILABLE);
|
||||
|
||||
/* wrong PQC type: fall back */
|
||||
info.pk.pqc_sign.type = WC_PQC_SIG_TYPE_MLDSA + 1;
|
||||
tpmCtx.mldsaKey = &dummyKey;
|
||||
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
||||
AssertIntEQ(rc, CRYPTOCB_UNAVAILABLE);
|
||||
|
||||
/* pre-hash: fall back, not pure ML-DSA */
|
||||
info.pk.pqc_sign.type = WC_PQC_SIG_TYPE_MLDSA;
|
||||
info.pk.pqc_sign.preHashType = WC_HASH_TYPE_SHA256;
|
||||
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
||||
AssertIntEQ(rc, CRYPTOCB_UNAVAILABLE);
|
||||
info.pk.pqc_sign.preHashType = WC_HASH_TYPE_NONE;
|
||||
|
||||
/* too big for one-shot: fall back */
|
||||
info.pk.pqc_sign.inlen = MAX_DIGEST_BUFFER + 1;
|
||||
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
||||
AssertIntEQ(rc, CRYPTOCB_UNAVAILABLE);
|
||||
info.pk.pqc_sign.inlen = (word32)sizeof(msg);
|
||||
|
||||
/* NULL outlen: reject before dereference */
|
||||
info.pk.pqc_sign.outlen = NULL;
|
||||
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
||||
AssertIntEQ(rc, CRYPTOCB_UNAVAILABLE);
|
||||
info.pk.pqc_sign.outlen = &sigLen;
|
||||
|
||||
/* happy path (needs v1.85 ML-DSA) */
|
||||
rc = wolfTPM2_GetKeyTemplate_MLDSA(&pub,
|
||||
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
||||
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
||||
TPMA_OBJECT_noDA, TPM_MLDSA_65, 0);
|
||||
if (rc == 0) {
|
||||
rc = wolfTPM2_CreatePrimaryKey(&dev, &tpmKey, TPM_RH_OWNER, &pub,
|
||||
NULL, 0);
|
||||
}
|
||||
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
||||
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
||||
/* TPM lacks ML-DSA: skip */
|
||||
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
||||
"CryptoDevCb ML-DSA sign:");
|
||||
}
|
||||
else {
|
||||
AssertIntEQ(rc, 0);
|
||||
bigSig = (byte*)XMALLOC(WC_MLDSA_65_SIG_SIZE, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
AssertNotNull(bigSig);
|
||||
tpmCtx.mldsaKey = &tpmKey;
|
||||
info.pk.pqc_sign.type = WC_PQC_SIG_TYPE_MLDSA;
|
||||
info.pk.pqc_sign.out = bigSig;
|
||||
|
||||
/* full buffer: signs, sets len */
|
||||
bigSigLen = (word32)WC_MLDSA_65_SIG_SIZE;
|
||||
info.pk.pqc_sign.outlen = &bigSigLen;
|
||||
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
||||
AssertIntEQ(rc, 0);
|
||||
AssertIntEQ((int)bigSigLen, WC_MLDSA_65_SIG_SIZE);
|
||||
|
||||
/* context passthrough: sig must verify under the same context */
|
||||
XMEMSET(signContext, 0x42, sizeof(signContext));
|
||||
bigSigLen = (word32)WC_MLDSA_65_SIG_SIZE;
|
||||
info.pk.pqc_sign.outlen = &bigSigLen;
|
||||
info.pk.pqc_sign.context = signContext;
|
||||
info.pk.pqc_sign.contextLen = (byte)sizeof(signContext);
|
||||
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
||||
AssertIntEQ(rc, 0);
|
||||
AssertIntEQ((int)bigSigLen, WC_MLDSA_65_SIG_SIZE);
|
||||
info.pk.pqc_sign.context = NULL;
|
||||
info.pk.pqc_sign.contextLen = 0;
|
||||
|
||||
#ifdef WOLFTPM_MLDSA_VERIFY
|
||||
rc = wolfTPM2_VerifySequenceStart(&dev, &tpmKey, signContext,
|
||||
(int)sizeof(signContext), &vSeq);
|
||||
AssertIntEQ(rc, 0);
|
||||
rc = wolfTPM2_VerifySequenceUpdate(&dev, vSeq, msg, (int)sizeof(msg));
|
||||
AssertIntEQ(rc, 0);
|
||||
XMEMSET(&vtk, 0, sizeof(vtk));
|
||||
rc = wolfTPM2_VerifySequenceComplete(&dev, vSeq, &tpmKey,
|
||||
NULL, 0, bigSig, (int)bigSigLen, &vtk);
|
||||
AssertIntEQ(rc, 0);
|
||||
|
||||
/* a different context must not verify the same signature */
|
||||
XMEMSET(otherContext, 0x24, sizeof(otherContext));
|
||||
rc = wolfTPM2_VerifySequenceStart(&dev, &tpmKey, otherContext,
|
||||
(int)sizeof(otherContext), &vSeq);
|
||||
AssertIntEQ(rc, 0);
|
||||
rc = wolfTPM2_VerifySequenceUpdate(&dev, vSeq, msg, (int)sizeof(msg));
|
||||
AssertIntEQ(rc, 0);
|
||||
XMEMSET(&vtk, 0, sizeof(vtk));
|
||||
rc = wolfTPM2_VerifySequenceComplete(&dev, vSeq, &tpmKey,
|
||||
NULL, 0, bigSig, (int)bigSigLen, &vtk);
|
||||
AssertIntNE(rc, 0);
|
||||
#endif /* WOLFTPM_MLDSA_VERIFY */
|
||||
|
||||
/* small buffer: preserves the size-error contract, not WC_HW_E */
|
||||
bigSigLen = 16;
|
||||
info.pk.pqc_sign.outlen = &bigSigLen;
|
||||
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
||||
AssertIntEQ(rc, BUFFER_E);
|
||||
|
||||
XFREE(bigSig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
wolfTPM2_UnloadHandle(&dev, &tpmKey.handle);
|
||||
printf("Test TPM Wrapper: %-40s Passed\n", "CryptoDevCb ML-DSA sign:");
|
||||
}
|
||||
|
||||
wolfTPM2_Cleanup(&dev);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void test_TPM2_ASN_DecodeX509Cert_Errors(void)
|
||||
{
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN)
|
||||
|
|
@ -6909,6 +7066,7 @@ int unit_tests(int argc, char *argv[])
|
|||
test_wolfTPM2_ReadPublicKey();
|
||||
test_wolfTPM2_CSR();
|
||||
test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS();
|
||||
test_wolfTPM2_CryptoDevCb_MlDsaSign();
|
||||
test_TPM2_ASN_DecodeX509Cert_Errors();
|
||||
test_TPM2_ASN_DecodeX509Cert_Valid();
|
||||
test_TPM2_ASN_DecodeTag_Errors();
|
||||
|
|
|
|||
|
|
@ -4339,6 +4339,9 @@ typedef struct TpmCryptoDevCtx {
|
|||
unsigned short useSymmetricOnTPM:1; /* if set indicates desire to use symmetric algorithms on TPM */
|
||||
#endif
|
||||
unsigned short useFIPSMode:1; /* if set requires FIPS mode on TPM and no fallback to software algos */
|
||||
#ifdef WOLFTPM_MLDSA_SIGN
|
||||
WOLFTPM2_KEY* mldsaKey; /* ML-DSA identity key; private key stays in TPM */
|
||||
#endif
|
||||
} TpmCryptoDevCtx;
|
||||
|
||||
#endif /* WOLFTPM_CRYPTOCB || HAVE_PK_CALLBACKS */
|
||||
|
|
|
|||
Loading…
Reference in New Issue