mirror of https://github.com/wolfSSL/wolfTPM.git
Merge pull request #559 from aidangarske/mldsa-tls-cryptocb
Add TPM MLDSA authentication for post quantum TLS 1.3 with example and testspull/562/head
commit
fa87ed478b
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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**:
|
||||
|
||||
```
|
||||
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,303 @@
|
|||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_CERT_GEN_CACHE
|
||||
/* wc_SetIssuerBuffer retains a decoded copy until this is called */
|
||||
wc_SetCert_Free(&leafCert);
|
||||
#endif
|
||||
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
|
||||
|
|
|
|||
|
|
@ -678,6 +678,49 @@ run_tpm_tls_server() { # Usage: run_tpm_tls_server [ecc/rsa] [tpmargs] [tlsversi
|
|||
popd >> $TPMPWD/run.out 2>&1
|
||||
}
|
||||
|
||||
|
||||
run_tpm_tls_pq() { # Usage: run_tpm_tls_pq [ML-KEM group] [ML-DSA set]
|
||||
echo -e "TLS test (TPM PQC) group $1 ML-DSA-$2"
|
||||
generate_port
|
||||
|
||||
# the TPM ML-DSA identity and its CA are regenerated per parameter set
|
||||
echo -e "./examples/pqc/gen_pqc_certs -mldsa=$2"
|
||||
./examples/pqc/gen_pqc_certs -mldsa=$2 >> $TPMPWD/run.out 2>&1
|
||||
RESULT=$?
|
||||
[ $RESULT -ne 0 ] && echo -e "gen_pqc_certs mldsa=$2 failed! $RESULT" && exit 1
|
||||
|
||||
echo -e "./examples/tls/tls_server -p=$port -mldsa=$2"
|
||||
./examples/tls/tls_server -p=$port -mldsa=$2 > $TPMPWD/pqtls.out 2>&1 &
|
||||
SERVER_PID=$!
|
||||
if ! wait_for_port "$port" 500; then
|
||||
echo -e "TPM PQC TLS server failed to start on port $port"
|
||||
kill $SERVER_PID 2>/dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "./examples/tls/tls_client -p=$port -mldsa -group=$1"
|
||||
./examples/tls/tls_client -p=$port -mldsa -group=$1 >> $TPMPWD/run.out 2>&1
|
||||
RESULT=$?
|
||||
if [ $RESULT -ne 0 ]; then
|
||||
kill $SERVER_PID 2>/dev/null
|
||||
wait $SERVER_PID 2>/dev/null
|
||||
cat $TPMPWD/pqtls.out >> $TPMPWD/run.out
|
||||
rm -f $TPMPWD/pqtls.out
|
||||
echo -e "tpm pqc tls $1 mldsa=$2 failed! $RESULT" && exit 1
|
||||
fi
|
||||
wait $SERVER_PID 2>/dev/null
|
||||
SRV_RESULT=$?
|
||||
cat $TPMPWD/pqtls.out >> $TPMPWD/run.out
|
||||
[ $SRV_RESULT -ne 0 ] && rm -f $TPMPWD/pqtls.out && \
|
||||
echo -e "tpm pqc tls server $1 mldsa=$2 failed! $SRV_RESULT" && exit 1
|
||||
|
||||
# this run's server must have signed on the TPM, not in software
|
||||
grep -q "signed on TPM" $TPMPWD/pqtls.out
|
||||
RESULT=$?
|
||||
rm -f $TPMPWD/pqtls.out
|
||||
[ $RESULT -ne 0 ] && echo -e "tpm pqc tls $1 mldsa=$2 did not sign on TPM!" && exit 1
|
||||
}
|
||||
|
||||
if [ $WOLFCRYPT_ENABLE -eq 1 ] && [ $WOLFCRYPT_DEFAULT -eq 0 ] && [ $NO_FILESYSTEM -eq 0 ]; then
|
||||
if [ $WOLFCRYPT_RSA -eq 1 ]; then
|
||||
# TLS client/server RSA TLS v1.2 and v1.3 Crypto callbacks
|
||||
|
|
@ -739,6 +782,24 @@ if [ $WOLFCRYPT_ENABLE -eq 1 ] && [ $WOLFCRYPT_DEFAULT -eq 0 ] && [ $NO_FILESYST
|
|||
fi
|
||||
fi
|
||||
|
||||
# TLS 1.3 with ML-KEM key exchange and a TPM held ML-DSA identity. Both ends
|
||||
# are wolfTPM examples, so no wolfSSL example pairing is needed here.
|
||||
# Requires a wolfSSL that routes wc_MlDsaKey_SignCtx to the crypto callback for
|
||||
# device keys; set ENABLE_PQC_TLS=1 once linked against one.
|
||||
if [ -z "$ENABLE_PQC_TLS" ]; then
|
||||
ENABLE_PQC_TLS=0
|
||||
fi
|
||||
if [ $ENABLE_V185 -eq 1 ] && [ $NO_FILESYSTEM -eq 0 ] && \
|
||||
[ $ENABLE_PQC_TLS -eq 1 ]; then
|
||||
run_tpm_tls_pq "ML_KEM_768" "65"
|
||||
run_tpm_tls_pq "ML_KEM_512" "44"
|
||||
run_tpm_tls_pq "ML_KEM_1024" "87"
|
||||
# hybrid groups need the classical curve half
|
||||
if [ $WOLFCRYPT_ECC -eq 1 ]; then
|
||||
run_tpm_tls_pq "SECP256R1MLKEM768" "65"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Clock Tests
|
||||
echo -e "Clock tests"
|
||||
|
|
|
|||
|
|
@ -89,10 +89,17 @@
|
|||
static void usage(void)
|
||||
{
|
||||
printf("Expected usage:\n");
|
||||
printf("./examples/tls/tls_client [-ecc/rsa] [-aes/xor]\n");
|
||||
printf("./examples/tls/tls_client [-ecc/rsa/mldsa] [-aes/xor]\n");
|
||||
printf("* -ecc: Use ECC key/cert\n");
|
||||
printf("* -rsa: Use RSA key/cert\n");
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
printf("* -mldsa: Validate a server TPM ML-DSA cert against the PQC CA\n");
|
||||
printf(" from gen_pqc_certs (TLS 1.3 only)\n");
|
||||
printf("* -group=NAME: ML_KEM_512/768/1024, SECP256R1MLKEM768 or\n");
|
||||
printf(" X25519MLKEM768 (default ML_KEM_768, implies -mldsa)\n");
|
||||
#endif
|
||||
printf("* -aes/xor: Use Parameter Encryption\n");
|
||||
printf("* -h=host: Server hostname (default %s)\n", TLS_HOST);
|
||||
printf("* -p=port: Supply a custom port number (default %d)\n", TLS_PORT);
|
||||
#if defined(WOLFTPM_CRYPTOCB) && defined(HAVE_PK_CALLBACKS)
|
||||
printf("* -pk: Use PK callbacks, not crypto callbacks\n");
|
||||
|
|
@ -133,8 +140,16 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
int total_size;
|
||||
int i;
|
||||
#endif
|
||||
const char* host = TLS_HOST;
|
||||
int hostGiven = 0;
|
||||
int useECC = 0;
|
||||
int usePK = 0;
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
int useMLDSA = 0;
|
||||
int kemGroup = WOLFSSL_ML_KEM_768;
|
||||
byte* pqCa = NULL;
|
||||
int pqCaSz = TLS_PQ_CERT_BUF_SZ;
|
||||
#endif
|
||||
TPM_ALG_ID paramEncAlg = TPM_ALG_NULL;
|
||||
WOLFTPM2_SESSION tpmSession;
|
||||
TPMT_PUBLIC publicTemplate;
|
||||
|
|
@ -172,7 +187,21 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
while (argc > 1) {
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (XSTRCMP(argv[argc-1], "-mldsa") == 0) {
|
||||
useMLDSA = 1;
|
||||
}
|
||||
else if (XSTRNCMP(argv[argc-1], "-group=", 7) == 0) {
|
||||
useMLDSA = 1;
|
||||
if (TlsParseKemGroup(argv[argc-1] + 7, &kemGroup) != 0) {
|
||||
usage();
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
else if (XSTRCMP(argv[argc-1], "-ecc") == 0) {
|
||||
#else
|
||||
if (XSTRCMP(argv[argc-1], "-ecc") == 0) {
|
||||
#endif
|
||||
useECC = 1;
|
||||
}
|
||||
else if (XSTRCMP(argv[argc-1], "-rsa") == 0) {
|
||||
|
|
@ -189,6 +218,10 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
usePK = 1;
|
||||
}
|
||||
#endif
|
||||
else if (XSTRNCMP(argv[argc-1], "-h=", XSTRLEN("-h=")) == 0) {
|
||||
host = argv[argc-1] + XSTRLEN("-h=");
|
||||
hostGiven = 1;
|
||||
}
|
||||
else if (XSTRNCMP(argv[argc-1], "-p=", XSTRLEN("-p=")) == 0) {
|
||||
const char* portStr = argv[argc-1] + XSTRLEN("-p=");
|
||||
port = (word32)XATOI(portStr);
|
||||
|
|
@ -199,6 +232,19 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
argc--;
|
||||
}
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA && usePK) {
|
||||
printf("-mldsa requires crypto callbacks; -pk has no ML-DSA route\n");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
if (useMLDSA && paramEncAlg != TPM_ALG_NULL) {
|
||||
/* the ML-DSA path uses no auth session, so -aes/-xor would be a
|
||||
* silently ignored claim rather than real parameter encryption */
|
||||
printf("-mldsa does not use a parameter encryption session\n");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("TPM2 TLS Client Example\n");
|
||||
printf("\tUse %s keys\n", useECC ? "ECC" : "RSA");
|
||||
printf("\tUse Parameter Encryption: %s\n", TPM2_GetAlgName(paramEncAlg));
|
||||
|
|
@ -212,7 +258,11 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
#ifdef NO_RSA
|
||||
if (!useECC) {
|
||||
if (!useECC
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
&& !useMLDSA
|
||||
#endif
|
||||
) {
|
||||
printf("RSA not compiled in!\n");
|
||||
return 0; /* don't report error */
|
||||
}
|
||||
|
|
@ -232,6 +282,10 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
#endif
|
||||
#ifdef HAVE_ECC
|
||||
tpmCtx.eccKey = &eccKey;
|
||||
#ifndef WOLFTPM2_USE_SW_ECDHE
|
||||
/* Ephemeral key, also used for the ECDHE half of a hybrid PQC group */
|
||||
tpmCtx.ecdhKey = &ecdhKey;
|
||||
#endif
|
||||
#endif
|
||||
tpmCtx.storageKey = &storageKey;
|
||||
#ifdef WOLFTPM_USE_SYMMETRIC
|
||||
|
|
@ -245,6 +299,14 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
/* The client only validates the server ML-DSA chain here, so no TPM
|
||||
* key of its own is needed. */
|
||||
goto tls_setup;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* See if primary storage key already exists */
|
||||
rc = getPrimaryStoragekey(&dev, &storageKey,
|
||||
useECC ? TPM_ALG_ECC : TPM_ALG_RSA);
|
||||
|
|
@ -326,16 +388,23 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
if (rc != 0) goto exit;
|
||||
}
|
||||
|
||||
#ifndef WOLFTPM2_USE_SW_ECDHE
|
||||
/* Ephemeral Key */
|
||||
tpmCtx.ecdhKey = &ecdhKey;
|
||||
#endif
|
||||
#endif /* HAVE_ECC */
|
||||
|
||||
|
||||
/* Setup the WOLFSSL context (factory)
|
||||
* Use highest version, allow downgrade */
|
||||
if ((ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())) == NULL) {
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
tls_setup:
|
||||
#endif
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
/* ML-DSA certificates are TLS 1.3 only */
|
||||
ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
|
||||
}
|
||||
else
|
||||
#endif
|
||||
ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
|
||||
if (ctx == NULL) {
|
||||
rc = MEMORY_E; goto exit;
|
||||
}
|
||||
|
||||
|
|
@ -353,9 +422,35 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
/* Require the server chain to validate against the PQC CA */
|
||||
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
|
||||
|
||||
pqCa = (byte*)XMALLOC(TLS_PQ_CERT_BUF_SZ, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (pqCa == NULL) {
|
||||
rc = MEMORY_E; goto exit;
|
||||
}
|
||||
rc = ReadDerFile(TLS_PQ_CA_CERT, pqCa, &pqCaSz);
|
||||
if (rc != 0) {
|
||||
printf("Run examples/pqc/gen_pqc_certs first\n");
|
||||
goto exit;
|
||||
}
|
||||
if (wolfSSL_CTX_load_verify_buffer(ctx, pqCa, pqCaSz,
|
||||
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
|
||||
printf("load_verify_buffer failed\n");
|
||||
rc = -1; goto exit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
/* Server certificate validation */
|
||||
/* Note: Can use "WOLFSSL_VERIFY_NONE" to skip peer cert validation */
|
||||
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, myVerify);
|
||||
/* myVerify overrides chain failures for local testing; do not apply that
|
||||
* override to a peer the user pointed us at explicitly. */
|
||||
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER,
|
||||
hostGiven ? NULL : myVerify);
|
||||
#ifdef NO_FILESYSTEM
|
||||
/* Load CA Certificates from Buffer */
|
||||
if (!useECC) {
|
||||
|
|
@ -516,6 +611,9 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
}
|
||||
#endif /* !NO_TLS_MUTUAL_AUTH */
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
}
|
||||
#endif
|
||||
#ifdef TLS_CIPHER_SUITE
|
||||
/* Optionally choose the cipher suite */
|
||||
rc = wolfSSL_CTX_set_cipher_list(ctx, TLS_CIPHER_SUITE);
|
||||
|
|
@ -537,8 +635,31 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
if (wolfSSL_UseKeyShare(ssl, (word16)kemGroup) != WOLFSSL_SUCCESS) {
|
||||
printf("UseKeyShare failed\n");
|
||||
rc = -1; goto exit;
|
||||
}
|
||||
/* advertise only the chosen group; no silent downgrade */
|
||||
if (wolfSSL_set_groups(ssl, &kemGroup, 1) != WOLFSSL_SUCCESS) {
|
||||
printf("set_groups failed\n");
|
||||
rc = -1; goto exit;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* An explicitly supplied host must match the certificate, or chaining to
|
||||
* a trusted CA alone would accept any peer that CA ever issued. */
|
||||
if (hostGiven) {
|
||||
if (wolfSSL_check_domain_name(ssl, host) != WOLFSSL_SUCCESS) {
|
||||
printf("check_domain_name failed for %s\n", host);
|
||||
rc = -1; goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup socket and connection */
|
||||
rc = SetupSocketAndConnect(&sockIoCtx, TLS_HOST, port);
|
||||
rc = SetupSocketAndConnect(&sockIoCtx, host, port);
|
||||
if (rc != 0) goto exit;
|
||||
|
||||
/* Setup read/write callback contexts */
|
||||
|
|
@ -558,6 +679,13 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[])
|
|||
if (rc != WOLFSSL_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
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");
|
||||
}
|
||||
#endif
|
||||
#ifdef TLS_BENCH_MODE
|
||||
benchStart = gettime_secs(0) - benchStart;
|
||||
printf("Connect: %9.3f sec (%9.3f CPS)\n", benchStart, 1/benchStart);
|
||||
|
|
@ -658,6 +786,9 @@ exit:
|
|||
|
||||
CloseAndCleanupSocket(&sockIoCtx);
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
XFREE(pqCa, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
#endif
|
||||
wolfTPM2_UnloadHandle(&dev, &storageKey.handle);
|
||||
#ifndef NO_RSA
|
||||
wolfTPM2_UnloadHandle(&dev, &rsaKey.handle);
|
||||
|
|
|
|||
|
|
@ -381,6 +381,84 @@ static inline void CloseAndCleanupSocket(SockIoCbCtx* sockIoCtx)
|
|||
/* --- BEGIN Supporting TLS functions --- */
|
||||
/******************************************************************************/
|
||||
|
||||
/* The PQC TLS mode needs more than TPM ML-DSA support: TLS 1.3 for the
|
||||
* certificate type, a filesystem for the DER identity, the crypto callback
|
||||
* that routes signing to the TPM, and private-key-id to reference it. */
|
||||
#if defined(WOLFTPM_MLDSA_SIGN) && defined(WOLFSSL_TLS13) && \
|
||||
defined(WOLFTPM_CRYPTOCB) && defined(WOLF_PRIVATE_KEY_ID) && \
|
||||
!defined(NO_FILESYSTEM) && \
|
||||
defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \
|
||||
!defined(WOLFSSL_MLDSA_NO_VERIFY) && \
|
||||
defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
||||
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
||||
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
|
||||
#define WOLFTPM_TLS_PQC
|
||||
#endif
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
/* PQC identity produced by examples/pqc/gen_pqc_certs */
|
||||
#define TLS_PQ_CA_CERT "./certs/pq-ca-cert.der"
|
||||
#define TLS_PQ_SERVER_CERT "./certs/pq-server-cert.der"
|
||||
#define TLS_PQ_CERT_BUF_SZ 10000
|
||||
|
||||
static inline int TlsParseMldsaSet(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;
|
||||
}
|
||||
|
||||
/* ML-KEM (and hybrid) key share groups for the TLS 1.3 PQC examples */
|
||||
static inline int TlsParseKemGroup(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;
|
||||
}
|
||||
|
||||
/* Read a DER file into der, updating derSz with the bytes read. */
|
||||
static inline int ReadDerFile(const char* file, byte* der, int* derSz)
|
||||
{
|
||||
XFILE f;
|
||||
long sz;
|
||||
|
||||
f = XFOPEN(file, "rb");
|
||||
if (f == XBADFILE) {
|
||||
printf("Cannot open %s\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;
|
||||
}
|
||||
#endif /* WOLFTPM_TLS_PQC */
|
||||
|
||||
static inline int myVerify(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
||||
{
|
||||
/* Verify Callback Arguments:
|
||||
|
|
|
|||
|
|
@ -113,9 +113,13 @@ static int mStop = 0;
|
|||
static void usage(void)
|
||||
{
|
||||
printf("Expected usage:\n");
|
||||
printf("./examples/tls/tls_server [-ecc/rsa] [-aes/xor]\n");
|
||||
printf("./examples/tls/tls_server [-ecc/rsa/mldsa] [-aes/xor]\n");
|
||||
printf("* -ecc: Use ECC key/cert\n");
|
||||
printf("* -rsa: Use RSA key/cert\n");
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
printf("* -mldsa[=44/65/87]: Use TPM ML-DSA key with the PQC cert from\n");
|
||||
printf(" gen_pqc_certs (TLS 1.3 only, default 65)\n");
|
||||
#endif
|
||||
printf("* -aes/xor: Use Parameter Encryption\n");
|
||||
printf("* -p=port: Supply a custom port number (default %d)\n", TLS_PORT);
|
||||
#if defined(WOLFTPM_CRYPTOCB) && defined(HAVE_PK_CALLBACKS)
|
||||
|
|
@ -173,6 +177,14 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
int usePK = 0;
|
||||
int runLoop = 0;
|
||||
int useSelfSign = 0;
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
int useMLDSA = 0;
|
||||
TPMI_MLDSA_PARAMETER_SET mldsaSet = TPM_MLDSA_65;
|
||||
WOLFTPM2_KEY mldsaKey;
|
||||
byte* pqCert = NULL;
|
||||
int pqCertSz = TLS_PQ_CERT_BUF_SZ;
|
||||
static const byte mldsaKeyId[] = { 't','p','m','m','l','d','s','a' };
|
||||
#endif
|
||||
TPM_ALG_ID paramEncAlg = TPM_ALG_NULL;
|
||||
WOLFTPM2_SESSION tpmSession;
|
||||
TPMT_PUBLIC publicTemplate;
|
||||
|
|
@ -198,6 +210,9 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
#endif
|
||||
#endif
|
||||
XMEMSET(&tpmSession, 0, sizeof(tpmSession));
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
XMEMSET(&mldsaKey, 0, sizeof(mldsaKey));
|
||||
#endif
|
||||
|
||||
if (argc >= 2) {
|
||||
if (XSTRCMP(argv[1], "-?") == 0 ||
|
||||
|
|
@ -214,6 +229,18 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
else if (XSTRCMP(argv[argc-1], "-rsa") == 0) {
|
||||
useECC = 0;
|
||||
}
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
else if (XSTRCMP(argv[argc-1], "-mldsa") == 0) {
|
||||
useMLDSA = 1;
|
||||
}
|
||||
else if (XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) {
|
||||
useMLDSA = 1;
|
||||
if (TlsParseMldsaSet(argv[argc-1] + 7, &mldsaSet) != 0) {
|
||||
usage();
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (XSTRCMP(argv[argc-1], "-aes") == 0) {
|
||||
paramEncAlg = TPM_ALG_CFB;
|
||||
}
|
||||
|
|
@ -241,7 +268,27 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
argc--;
|
||||
}
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA && usePK) {
|
||||
printf("-mldsa requires crypto callbacks; -pk has no ML-DSA route\n");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
if (useMLDSA && paramEncAlg != TPM_ALG_NULL) {
|
||||
/* the ML-DSA path uses no auth session, so -aes/-xor would be a
|
||||
* silently ignored claim rather than real parameter encryption */
|
||||
printf("-mldsa does not use a parameter encryption session\n");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("TPM2 TLS Server Example\n");
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
printf("\tUse ML-DSA-%d keys\n",
|
||||
mldsaSet == TPM_MLDSA_44 ? 44 : (mldsaSet == TPM_MLDSA_87 ? 87 : 65));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
printf("\tUse %s keys\n", useECC ? "ECC" : "RSA");
|
||||
printf("\tUse Parameter Encryption: %s\n", TPM2_GetAlgName(paramEncAlg));
|
||||
printf("\tUsing Port: %d\n", port);
|
||||
|
|
@ -254,7 +301,11 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
#ifdef NO_RSA
|
||||
if (!useECC) {
|
||||
if (!useECC
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
&& !useMLDSA
|
||||
#endif
|
||||
) {
|
||||
printf("RSA not compiled in!\n");
|
||||
return 0; /* don't report error */
|
||||
}
|
||||
|
|
@ -274,8 +325,17 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
#endif
|
||||
#ifdef HAVE_ECC
|
||||
tpmCtx.eccKey = &eccKey;
|
||||
#ifndef WOLFTPM2_USE_SW_ECDHE
|
||||
/* Ephemeral key, also used for the ECDHE half of a hybrid PQC group */
|
||||
tpmCtx.ecdhKey = &ecdhKey;
|
||||
#endif
|
||||
#endif
|
||||
tpmCtx.storageKey = &storageKey;
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
tpmCtx.mldsaKey = &mldsaKey;
|
||||
}
|
||||
#endif
|
||||
#ifdef WOLFTPM_USE_SYMMETRIC
|
||||
tpmCtx.useSymmetricOnTPM = 1;
|
||||
#endif
|
||||
|
|
@ -287,6 +347,27 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
/* Recreate the same TPM ML-DSA identity key gen_pqc_certs certified.
|
||||
* It is a primary key, so no storage parent is involved. */
|
||||
rc = wolfTPM2_GetKeyTemplate_MLDSA(&publicTemplate,
|
||||
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
||||
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
||||
TPMA_OBJECT_noDA, mldsaSet, 0);
|
||||
if (rc == 0) {
|
||||
rc = wolfTPM2_CreatePrimaryKey(&dev, &mldsaKey, TPM_RH_OWNER,
|
||||
&publicTemplate, NULL, 0);
|
||||
}
|
||||
if (rc != 0) {
|
||||
printf("Create TPM ML-DSA key failed 0x%x: %s\n",
|
||||
rc, wolfTPM2_GetRCString(rc));
|
||||
goto exit;
|
||||
}
|
||||
goto tls_setup;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* See if primary storage key already exists */
|
||||
rc = getPrimaryStoragekey(&dev, &storageKey,
|
||||
useECC ? TPM_ALG_ECC : TPM_ALG_RSA);
|
||||
|
|
@ -368,16 +449,23 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
if (rc != 0) goto exit;
|
||||
}
|
||||
|
||||
#ifndef WOLFTPM2_USE_SW_ECDHE
|
||||
/* Ephemeral Key */
|
||||
tpmCtx.ecdhKey = &ecdhKey;
|
||||
#endif
|
||||
#endif /* HAVE_ECC */
|
||||
|
||||
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
tls_setup:
|
||||
#endif
|
||||
/* Setup the WOLFSSL context (factory)
|
||||
* Use highest version, allow downgrade */
|
||||
if ((ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())) == NULL) {
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
/* ML-DSA certificates are TLS 1.3 only */
|
||||
ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
|
||||
}
|
||||
else
|
||||
#endif
|
||||
ctx = wolfSSL_CTX_new(wolfSSLv23_server_method());
|
||||
if (ctx == NULL) {
|
||||
rc = MEMORY_E; goto exit;
|
||||
}
|
||||
|
||||
|
|
@ -394,6 +482,36 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
wolfTPM_PK_SetCb(ctx);
|
||||
}
|
||||
#endif
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
/* The PQC example does not do client auth; the client validates the
|
||||
* server ML-DSA chain against the CA from gen_pqc_certs. */
|
||||
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL);
|
||||
|
||||
pqCert = (byte*)XMALLOC(TLS_PQ_CERT_BUF_SZ, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (pqCert == NULL) {
|
||||
rc = MEMORY_E; goto exit;
|
||||
}
|
||||
rc = ReadDerFile(TLS_PQ_SERVER_CERT, pqCert, &pqCertSz);
|
||||
if (rc != 0) {
|
||||
printf("Run examples/pqc/gen_pqc_certs first\n");
|
||||
goto exit;
|
||||
}
|
||||
if (wolfSSL_CTX_use_certificate_buffer(ctx, pqCert, pqCertSz,
|
||||
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
|
||||
printf("use_certificate_buffer failed\n");
|
||||
rc = -1; goto exit;
|
||||
}
|
||||
/* private key stays in the TPM: reference it by id + devId */
|
||||
if (wolfSSL_CTX_use_PrivateKey_Id(ctx, mldsaKeyId,
|
||||
sizeof(mldsaKeyId), tpmDevId) != WOLFSSL_SUCCESS) {
|
||||
printf("use_PrivateKey_Id failed\n");
|
||||
rc = -1; goto exit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
|
||||
/* Server certificate validation */
|
||||
/* Note: Can use "WOLFSSL_VERIFY_NONE" to skip peer cert validation */
|
||||
|
|
@ -547,6 +665,9 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
goto exit;
|
||||
#endif /* HAVE_ECC */
|
||||
}
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TLS_CIPHER_SUITE
|
||||
|
|
@ -604,6 +725,12 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[])
|
|||
if (rc != WOLFSSL_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
if (useMLDSA) {
|
||||
printf("Handshake: %s, group %s (ML-DSA identity signed on TPM)\n",
|
||||
wolfSSL_get_cipher(ssl), wolfSSL_get_curve_name(ssl));
|
||||
}
|
||||
#endif
|
||||
#ifdef TLS_BENCH_MODE
|
||||
benchStart = gettime_secs(0) - benchStart;
|
||||
printf("Accept: %9.3f sec (%9.3f CPS)\n", benchStart, 1/benchStart);
|
||||
|
|
@ -705,6 +832,10 @@ exit:
|
|||
CloseAndCleanupSocket(&sockIoCtx);
|
||||
|
||||
wolfTPM2_UnloadHandle(&dev, &storageKey.handle);
|
||||
#ifdef WOLFTPM_TLS_PQC
|
||||
wolfTPM2_UnloadHandle(&dev, &mldsaKey.handle);
|
||||
XFREE(pqCert, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
#endif
|
||||
#ifndef NO_RSA
|
||||
wolfTPM2_UnloadHandle(&dev, &rsaKey.handle);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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,56 @@ 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;
|
||||
|
||||
/* Not an ML-DSA request, or no TPM key configured: software can
|
||||
* legitimately take it. */
|
||||
if (info->pk.pqc_sign.type != WC_PQC_SIG_TYPE_MLDSA ||
|
||||
tlsCtx->mldsaKey == NULL) {
|
||||
return exit_rc;
|
||||
}
|
||||
/* The key lives in the TPM, so a software fallback would sign with
|
||||
* absent private material. Fail instead of yielding. Only the pure
|
||||
* one-shot form is supported here. */
|
||||
if (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) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -4917,6 +4920,164 @@ 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);
|
||||
|
||||
/* With a TPM key set the private material is on-chip, so an unsupported
|
||||
* request must fail rather than yield to a software signer that has no
|
||||
* private key to use. */
|
||||
|
||||
/* pre-hash: 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, BAD_FUNC_ARG);
|
||||
info.pk.pqc_sign.preHashType = WC_HASH_TYPE_NONE;
|
||||
|
||||
/* too big for one-shot */
|
||||
info.pk.pqc_sign.inlen = MAX_DIGEST_BUFFER + 1;
|
||||
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
||||
AssertIntEQ(rc, BAD_FUNC_ARG);
|
||||
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, BAD_FUNC_ARG);
|
||||
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)
|
||||
|
|
@ -7309,6 +7470,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_RsaUnpadPkcsv15();
|
||||
test_TPM2_ASN_DecodeX509Cert_Valid();
|
||||
|
|
|
|||
|
|
@ -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