From 81a4851db0457377d6d8913e1dde9ba23fef1994 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 18 Jun 2026 08:34:44 -0700 Subject: [PATCH 1/9] Fix bound session parameter encryption key derivation --- src/tpm2_param_enc.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/tpm2_param_enc.c b/src/tpm2_param_enc.c index 4f9b1198..0c94e87a 100644 --- a/src/tpm2_param_enc.c +++ b/src/tpm2_param_enc.c @@ -180,6 +180,34 @@ int TPM2_ParamEnc_AESCFB( #ifndef WOLFTPM_FWTPM +/* Determine the extra key (authValue) to concatenate to the session key when + * deriving the parameter-encryption key. Per TPM 2.0 (Part 1 "Symmetric + * Encrypt" / the reference CryptParameterDecryption), that key is + * sessionKey || authValue(the entity the encrypt/decrypt session authorizes), + * "no matter if the handle is the session bound entity or not"; for a session + * that authorizes no handle (secondary encrypt-only session) it is just + * sessionKey. + * + * In this client the authorized handle's authValue is normally already folded + * into session->auth (via wolfTPM2_SetAuthHandle, making auth.size larger than + * the session-key digest). The exception is when the session is BOUND to the + * very entity it authorizes: the bind rule excludes that authValue from the + * HMAC key, so session->auth holds only the sessionKey (size == digest) and the + * needed authValue is available in session->bind. So append session->bind only + * in that bound-entity case: bound, authorizing a handle (name set), and the + * handle auth not already present. A secondary encrypt-only session (no + * authorized handle, name.size == 0) or one authorizing a different entity + * (auth already extended) must not add the bind authValue. */ +static TPM2B_AUTH* TPM2_ParamEncBindKey(TPM2_AUTH_SESSION* session) +{ + int digestSz = TPM2_GetHashDigestSize(session->authHash); + if (session->bind != NULL && session->name.size > 0 && + digestSz > 0 && session->auth.size <= (UINT16)digestSz) { + return session->bind; + } + return NULL; +} + /* Build combined HMAC key from session key + bind key */ static int TPM2_BuildParamKey(TPM2B_AUTH* sessKey, TPM2B_AUTH* bindKey, BYTE* keyBuf, UINT32* keyBufSz) @@ -229,7 +257,8 @@ TPM_RC TPM2_ParamEnc_CmdRequest(TPM2_AUTH_SESSION *session, TPM2_PrintBin(session->nonceTPM.buffer, session->nonceTPM.size); #endif - rc = TPM2_BuildParamKey(&session->auth, session->bind, keyBuf, &keyBufSz); + rc = TPM2_BuildParamKey(&session->auth, TPM2_ParamEncBindKey(session), + keyBuf, &keyBufSz); if (rc != 0) { return rc; } @@ -280,7 +309,8 @@ TPM_RC TPM2_ParamDec_CmdResponse(TPM2_AUTH_SESSION *session, TPM2_PrintBin(session->nonceTPM.buffer, session->nonceTPM.size); #endif - rc = TPM2_BuildParamKey(&session->auth, session->bind, keyBuf, &keyBufSz); + rc = TPM2_BuildParamKey(&session->auth, TPM2_ParamEncBindKey(session), + keyBuf, &keyBufSz); if (rc != 0) { return rc; } From 96ddbf55df08d01832cd07c0dc483678d377f9f5 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 18 Jun 2026 08:34:44 -0700 Subject: [PATCH 2/9] Compute sessionKey for bound EmptyAuth sessions --- src/fwtpm/fwtpm_command.c | 8 ++++++-- src/tpm2_wrap.c | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index c7c23adb..d06c86d6 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -8303,11 +8303,15 @@ static TPM_RC FwCmd_StartAuthSession(FWTPM_CTX* ctx, TPM2_Packet* cmd, TPM2_ForceZero(&bindAuth, sizeof(bindAuth)); } - if (keyInSz == 0) { - /* Unsalted, unbound: sessionKey is empty per spec */ + if (saltSize == 0 && bind == TPM_RH_NULL) { + /* Unsalted AND unbound: sessionKey is empty per spec */ sess->sessionKey.size = 0; } else { + /* Salted and/or bound: compute the sessionKey. For a session + * bound to an entity with an EmptyAuth, keyInSz is 0 here and + * KDFa runs over a zero-length key input - the sessionKey is + * still computed (Part 1 Bound Session Key Generation). */ int sessKeyRc; sess->sessionKey.size = (UINT16)nonceSize; sessKeyRc = TPM2_KDFa_ex(authHash, diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index bc1d8de1..204e4571 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -2612,7 +2612,12 @@ int wolfTPM2_StartSession(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, } } - if (rc == TPM_RC_SUCCESS && keyIn.size > 0) { + /* Compute the sessionKey when the session is salted or bound. Per TPM 2.0 + * Part 1 (Bound Session Key Generation), a bound session to an entity with + * an EmptyAuth still has its sessionKey computed - here keyIn is the empty + * bind authValue, so KDFa runs with a zero-length key input. Only a fully + * unbound, unsalted session keeps an empty sessionKey. */ + if (rc == TPM_RC_SUCCESS && (keyIn.size > 0 || bind != NULL)) { session->handle.auth.size = hashDigestSz; rc = TPM2_KDFa_ex(authSesIn.authHash, keyIn.buffer, keyIn.size, "ATH", From 348416afe897e9c017b2ebe2030bbfe7111526e1 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 18 Jun 2026 08:34:45 -0700 Subject: [PATCH 3/9] Add ML-DSA support to create_primary example --- examples/keygen/create_primary.c | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/examples/keygen/create_primary.c b/examples/keygen/create_primary.c index b6c0c4a1..b384a75e 100644 --- a/examples/keygen/create_primary.c +++ b/examples/keygen/create_primary.c @@ -47,6 +47,10 @@ static void usage(void) printf("Primary Key Type:\n"); printf("\t-rsa: Use RSA for asymmetric key generation (DEFAULT)\n"); printf("\t-ecc: Use ECC for asymmetric key generation \n"); +#ifdef WOLFTPM_PQC + printf("\t-mldsa[=44|65|87]: Use ML-DSA for primary key generation " + "(v1.85, default 65)\n"); +#endif printf("Hierarchy:\n"); printf("\t-oh: Create keys under the Owner Hierarchy (DEFAULT)\n"); printf("\t-eh: Create keys under the Endorsement Hierarchy\n"); @@ -77,7 +81,25 @@ static void usage(void) printf("\t* Create SRK used by wolfTPM:\n"); printf("\t\tcreate_primary -rsa -oh -auth=ThisIsMyStorageKeyAuth " "-store=0x81000200\n"); +#ifdef WOLFTPM_PQC + printf("\t* Create an ML-DSA-65 primary key:\n"); + printf("\t\tcreate_primary -mldsa -oh\n"); +#endif +} + +#ifdef WOLFTPM_PQC +static int mldsaParamSet(const char* optVal, TPMI_MLDSA_PARAMETER_SET* ps) +{ + int n = XATOI(optVal); + switch (n) { + case 0: /* missing or empty suffix, use default */ + case 65: *ps = TPM_MLDSA_65; return TPM_RC_SUCCESS; + case 44: *ps = TPM_MLDSA_44; return TPM_RC_SUCCESS; + case 87: *ps = TPM_MLDSA_87; return TPM_RC_SUCCESS; + default: return TPM_RC_FAILURE; + } } +#endif /* WOLFTPM_PQC */ int TPM2_CreatePrimaryKey_Example(void* userCtx, int argc, char *argv[]) { @@ -98,6 +120,9 @@ int TPM2_CreatePrimaryKey_Example(void* userCtx, int argc, char *argv[]) #ifdef WOLFTPM_PROVISIONING int useIAKTemplate = 0, useIDevIDTemplate = 0; #endif +#ifdef WOLFTPM_PQC + TPMI_MLDSA_PARAMETER_SET mldsaPs = TPM_MLDSA_65; /* default param set */ +#endif if (argc >= 2) { if (XSTRCMP(argv[1], "-?") == 0 || @@ -114,6 +139,19 @@ int TPM2_CreatePrimaryKey_Example(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-ecc") == 0) { alg = TPM_ALG_ECC; } +#ifdef WOLFTPM_PQC + else if (XSTRCMP(argv[argc-1], "-mldsa") == 0 || + XSTRNCMP(argv[argc-1], "-mldsa=", + XSTRLEN("-mldsa=")) == 0) { + const char* optVal = (argv[argc-1][6] == '=') ? + argv[argc-1] + 7 : ""; + if (mldsaParamSet(optVal, &mldsaPs) != TPM_RC_SUCCESS) { + usage(); + return 0; + } + alg = TPM_ALG_MLDSA; + } +#endif else if (XSTRCMP(argv[argc-1], "-aes") == 0) { paramEncAlg = TPM_ALG_CFB; } @@ -221,6 +259,17 @@ int TPM2_CreatePrimaryKey_Example(void* userCtx, int argc, char *argv[]) else rc = wolfTPM2_GetKeyTemplate_ECC_SRK(&publicTemplate); } +#ifdef WOLFTPM_PQC + else if (alg == TPM_ALG_MLDSA) { + /* ML-DSA is sign-only and has no AIK/IAK/IDevID variant; the + * wrapper enforces TPMA_OBJECT_sign and clears decrypt. */ + rc = wolfTPM2_GetKeyTemplate_MLDSA(&publicTemplate, + TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | + TPMA_OBJECT_fixedParent | TPMA_OBJECT_sensitiveDataOrigin | + TPMA_OBJECT_userWithAuth | TPMA_OBJECT_noDA, + mldsaPs, 0 /* allowExternalMu: 0 avoids TPM_RC_EXT_MU */); + } +#endif else { rc = BAD_FUNC_ARG; } From 8e6efcce719cd8905cab7d0a94852b9a2f57895c Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 18 Jun 2026 08:34:45 -0700 Subject: [PATCH 4/9] Add PQC key parameter encryption to examples --- examples/keygen/keygen.c | 70 ++++++++++++++++++++++++++++--- examples/nvram/counter.c | 52 +++++++++++++++++++++-- examples/nvram/store.c | 52 +++++++++++++++++++++-- examples/pcr/quote.c | 52 +++++++++++++++++++++-- examples/run_examples.sh | 33 +++++++++++++++ examples/tpm_test_keys.c | 86 +++++++++++++++++++++++++++++++++++++++ examples/tpm_test_keys.h | 15 +++++++ examples/wrap/include.am | 3 +- examples/wrap/wrap_test.c | 54 ++++++++++++++++++++++-- 9 files changed, 398 insertions(+), 19 deletions(-) diff --git a/examples/keygen/keygen.c b/examples/keygen/keygen.c index 2c87065c..931317da 100644 --- a/examples/keygen/keygen.c +++ b/examples/keygen/keygen.c @@ -84,6 +84,12 @@ static void usage(void) #endif printf("* -t: Use default template (otherwise AIK)\n"); printf("* -aes/xor: Use Parameter Encryption\n"); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + printf("* -paramkey=mlkem[=512|768|1024]: Use an ML-KEM key as the " + "param-enc session salt (v1.85, default 768)\n"); + printf("* -paramkey=mldsa[=44|65|87]: Use an ML-DSA key as the " + "param-enc session bind (v1.85, default 65)\n"); +#endif printf("* -unique=[value]\n"); printf("\t* Used for the KDF of the create\n"); printf("* -auth=pass: Use custom password for key authentication\n"); @@ -176,6 +182,11 @@ int TPM2_Keygen_Example(void* userCtx, int argc, char *argv[]) TPMI_ALG_PUBLIC srkAlg = TPM_ALG_RSA; /* default matches seal.c / keyload.c */ TPM_ALG_ID algSym = TPM_ALG_CTR; /* default Symmetric Cipher, see usage */ TPM_ALG_ID paramEncAlg = TPM_ALG_NULL; +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + TPM_ALG_ID pqcParamEncAlg = TPM_ALG_NULL; + int pqcParamSet = 0; + WOLFTPM2_KEY pqcKey; +#endif #ifdef WOLFTPM_PQC TPMI_MLDSA_PARAMETER_SET mldsaPs = TPM_MLDSA_65; /* default */ TPMI_MLKEM_PARAMETER_SET mlkemPs = TPM_MLKEM_768; /* default */ @@ -281,6 +292,38 @@ int TPM2_Keygen_Example(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-xor") == 0) { paramEncAlg = TPM_ALG_XOR; } +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + else if (XSTRNCMP(argv[argc-1], "-paramkey=", + XSTRLEN("-paramkey=")) == 0) { + /* Choose a PQC key for the param-enc session (separate from the + * child key -mlkem/-mldsa selection above). Value is e.g. + * "mlkem", "mlkem=768", "mldsa" or "mldsa=65". */ + const char* pkVal = argv[argc-1] + XSTRLEN("-paramkey="); + #ifdef WOLFTPM_MLKEM + if (XSTRNCMP(pkVal, "mlkem", 5) == 0) { + int n = (pkVal[5] == '=') ? XATOI(&pkVal[6]) : 768; + pqcParamEncAlg = TPM_ALG_MLKEM; + pqcParamSet = (n == 512) ? TPM_MLKEM_512 : + (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; + } + else + #endif + #ifdef WOLFTPM_MLDSA + if (XSTRNCMP(pkVal, "mldsa", 5) == 0) { + int n = (pkVal[5] == '=') ? XATOI(&pkVal[6]) : 65; + pqcParamEncAlg = TPM_ALG_MLDSA; + pqcParamSet = (n == 44) ? TPM_MLDSA_44 : + (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; + } + else + #endif + { + printf("Invalid -paramkey value: %s\n", pkVal); + usage(); + return 0; + } + } +#endif else if (XSTRNCMP(argv[argc-1], "-unique=", XSTRLEN("-unique=")) == 0) { uniqueStr = argv[argc-1] + XSTRLEN("-unique="); } @@ -310,6 +353,9 @@ int TPM2_Keygen_Example(void* userCtx, int argc, char *argv[]) XMEMSET(&primaryBlob, 0, sizeof(primaryBlob)); XMEMSET(&tpmSession, 0, sizeof(tpmSession)); XMEMSET(&auth, 0, sizeof(auth)); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + XMEMSET(&pqcKey, 0, sizeof(pqcKey)); +#endif /* Only use the ECC SRK for ECC child keys; RSA, SYMCIPHER, KEYEDHASH * all stay on the RSA SRK so that keyload/seal can round-trip them. */ @@ -364,9 +410,20 @@ int TPM2_Keygen_Example(void* userCtx, int argc, char *argv[]) if (srkAlg == TPM_ALG_RSA) bindKey = NULL; /* cannot bind to key without RSA enabled */ #endif - /* Start an authenticated session (salted / unbound) with parameter encryption */ - rc = wolfTPM2_StartSession(&dev, &tpmSession, bindKey, NULL, - TPM_SE_HMAC, paramEncAlg); + #if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + if (pqcParamEncAlg != TPM_ALG_NULL) { + /* Use a PQC primary as the param-enc session key: ML-KEM salt + * or ML-DSA bind. This replaces the salted-to-SRK session. */ + rc = getPrimaryParamEncKey(&dev, &tpmSession, &pqcKey, + pqcParamEncAlg, pqcParamSet, paramEncAlg); + } + else + #endif + { + /* Start an authenticated session (salted / unbound) with parameter encryption */ + rc = wolfTPM2_StartSession(&dev, &tpmSession, bindKey, NULL, + TPM_SE_HMAC, paramEncAlg); + } if (rc != 0) goto exit; printf("HMAC Session: Handle 0x%x\n", (word32)tpmSession.handle.hndl); @@ -462,9 +519,9 @@ int TPM2_Keygen_Example(void* userCtx, int argc, char *argv[]) TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent | TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth | TPMA_OBJECT_noDA, - mldsaPs, 0 /* allowExternalMu — TPM_RC_EXT_MU at create + mldsaPs, 0 /* allowExternalMu - TPM_RC_EXT_MU at create * per Part 2 Sec.12.2.3.6 when SET on a TPM - * without μ-direct sign support */); + * without mu-direct sign support */); } else if (alg == TPM_ALG_HASH_MLDSA) { printf("Hash-ML-DSA template (parameter set %u, pre-hash %s)\n", @@ -613,6 +670,9 @@ int TPM2_Keygen_Example(void* userCtx, int argc, char *argv[]) wolfTPM2_UnloadHandle(&dev, &primary->handle); wolfTPM2_UnloadHandle(&dev, &newKeyBlob.handle); wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + wolfTPM2_UnloadHandle(&dev, &pqcKey.handle); +#endif wolfTPM2_Cleanup(&dev); return rc; diff --git a/examples/nvram/counter.c b/examples/nvram/counter.c index 80f04563..9b133578 100644 --- a/examples/nvram/counter.c +++ b/examples/nvram/counter.c @@ -50,6 +50,12 @@ static void usage(void) printf("./examples/nvram/counter [-nvindex=] [-aes/-xor]\n"); printf("* -nvindex=[handle] (default 0x%x)\n", TPM2_DEMO_NV_COUNTER_INDEX); printf("* -aes/xor: Use Parameter Encryption\n"); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + printf("* -mlkem[=512|768|1024]: Use an ML-KEM key as the param-enc " + "session salt (v1.85, default 768)\n"); + printf("* -mldsa[=44|65|87]: Use an ML-DSA key as the param-enc " + "session bind (v1.85, default 65)\n"); +#endif } int TPM2_NVRAM_Counter_Example(void* userCtx, int argc, char *argv[]) @@ -63,12 +69,20 @@ int TPM2_NVRAM_Counter_Example(void* userCtx, int argc, char *argv[]) TPMS_NV_PUBLIC nvPublic; TPMI_RH_NV_AUTH authHandle = TPM_RH_OWNER; /* or TPM_RH_PLATFORM */ int paramEncAlg = TPM_ALG_NULL; +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + TPM_ALG_ID pqcParamEncAlg = TPM_ALG_NULL; + int pqcParamSet = 0; + WOLFTPM2_KEY pqcKey; +#endif word32 nvIndex = TPM2_DEMO_NV_COUNTER_INDEX; XMEMSET(&tpmSession, 0, sizeof(tpmSession)); XMEMSET(&parent, 0, sizeof(parent)); XMEMSET(&nv, 0, sizeof(nv)); XMEMSET(&storage, 0, sizeof(storage)); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + XMEMSET(&pqcKey, 0, sizeof(pqcKey)); +#endif if (argc >= 2) { if (XSTRCMP(argv[1], "-?") == 0 || @@ -104,6 +118,24 @@ int TPM2_NVRAM_Counter_Example(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-xor") == 0) { paramEncAlg = TPM_ALG_XOR; } +#ifdef WOLFTPM_MLKEM + else if (XSTRCMP(argv[argc-1], "-mlkem") == 0 || + XSTRNCMP(argv[argc-1], "-mlkem=", 7) == 0) { + int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 768; + pqcParamEncAlg = TPM_ALG_MLKEM; + pqcParamSet = (n == 512) ? TPM_MLKEM_512 : + (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; + } +#endif +#ifdef WOLFTPM_MLDSA + else if (XSTRCMP(argv[argc-1], "-mldsa") == 0 || + XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) { + int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 65; + pqcParamEncAlg = TPM_ALG_MLDSA; + pqcParamSet = (n == 44) ? TPM_MLDSA_44 : + (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; + } +#endif else { printf("Warning: Unrecognized option: %s\n", argv[argc-1]); } @@ -130,9 +162,20 @@ int TPM2_NVRAM_Counter_Example(void* userCtx, int argc, char *argv[]) } if (paramEncAlg != TPM_ALG_NULL) { - /* Start TPM session for parameter encryption */ - rc = wolfTPM2_StartSession(&dev, &tpmSession, NULL, NULL, - TPM_SE_HMAC, paramEncAlg); + #if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + if (pqcParamEncAlg != TPM_ALG_NULL) { + /* Use a PQC primary as the param-enc session key: ML-KEM salt + * or ML-DSA bind. */ + rc = getPrimaryParamEncKey(&dev, &tpmSession, &pqcKey, + pqcParamEncAlg, pqcParamSet, paramEncAlg); + } + else + #endif + { + /* Start TPM session for parameter encryption */ + rc = wolfTPM2_StartSession(&dev, &tpmSession, NULL, NULL, + TPM_SE_HMAC, paramEncAlg); + } if (rc != 0) goto exit; printf("TPM2_StartAuthSession: sessionHandle 0x%x\n", (word32)tpmSession.handle.hndl); @@ -191,6 +234,9 @@ int TPM2_NVRAM_Counter_Example(void* userCtx, int argc, char *argv[]) } wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + wolfTPM2_UnloadHandle(&dev, &pqcKey.handle); +#endif wolfTPM2_Cleanup(&dev); return rc; diff --git a/examples/nvram/store.c b/examples/nvram/store.c index ba965901..e49d48ec 100644 --- a/examples/nvram/store.c +++ b/examples/nvram/store.c @@ -57,6 +57,12 @@ static void usage(void) printf("* -priv: Store only the private part of the key\n"); printf("* -pub: Store only the public part of the key\n"); printf("* -aes/xor: Use Parameter Encryption\n"); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + printf("* -mlkem[=512|768|1024]: Use an ML-KEM key as the param-enc " + "session salt (v1.85, default 768)\n"); + printf("* -mldsa[=44|65|87]: Use an ML-DSA key as the param-enc " + "session bind (v1.85, default 65)\n"); +#endif } int TPM2_NVRAM_Store_Example(void* userCtx, int argc, char *argv[]) @@ -71,6 +77,11 @@ int TPM2_NVRAM_Store_Example(void* userCtx, int argc, char *argv[]) TPMI_RH_NV_AUTH authHandle = TPM_RH_OWNER; /* or TPM_RH_PLATFORM */ const char* filename = "keyblob.bin"; int paramEncAlg = TPM_ALG_NULL; +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + TPM_ALG_ID pqcParamEncAlg = TPM_ALG_NULL; + int pqcParamSet = 0; + WOLFTPM2_KEY pqcKey; +#endif int partialStore = 0; int offset = 0; /* Needed for TPM2_AppendPublic */ @@ -121,6 +132,24 @@ int TPM2_NVRAM_Store_Example(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-pub") == 0) { partialStore = PUBLIC_PART_ONLY; } +#ifdef WOLFTPM_MLKEM + else if (XSTRCMP(argv[argc-1], "-mlkem") == 0 || + XSTRNCMP(argv[argc-1], "-mlkem=", 7) == 0) { + int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 768; + pqcParamEncAlg = TPM_ALG_MLKEM; + pqcParamSet = (n == 512) ? TPM_MLKEM_512 : + (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; + } +#endif +#ifdef WOLFTPM_MLDSA + else if (XSTRCMP(argv[argc-1], "-mldsa") == 0 || + XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) { + int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 65; + pqcParamEncAlg = TPM_ALG_MLDSA; + pqcParamSet = (n == 44) ? TPM_MLDSA_44 : + (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; + } +#endif else if (argv[argc-1][0] != '-') { filename = argv[argc-1]; } @@ -144,6 +173,9 @@ int TPM2_NVRAM_Store_Example(void* userCtx, int argc, char *argv[]) XMEMSET(&keyBlob, 0, sizeof(keyBlob)); XMEMSET(&tpmSession, 0, sizeof(tpmSession)); XMEMSET(&parent, 0, sizeof(parent)); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + XMEMSET(&pqcKey, 0, sizeof(pqcKey)); +#endif rc = wolfTPM2_Init(&dev, TPM2_IoCb, userCtx); if (rc != TPM_RC_SUCCESS) { @@ -152,9 +184,20 @@ int TPM2_NVRAM_Store_Example(void* userCtx, int argc, char *argv[]) } if (paramEncAlg != TPM_ALG_NULL) { - /* Start TPM session for parameter encryption */ - rc = wolfTPM2_StartSession(&dev, &tpmSession, NULL, NULL, - TPM_SE_HMAC, paramEncAlg); + #if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + if (pqcParamEncAlg != TPM_ALG_NULL) { + /* Use a PQC primary as the param-enc session key: ML-KEM salt + * or ML-DSA bind. */ + rc = getPrimaryParamEncKey(&dev, &tpmSession, &pqcKey, + pqcParamEncAlg, pqcParamSet, paramEncAlg); + } + else + #endif + { + /* Start TPM session for parameter encryption */ + rc = wolfTPM2_StartSession(&dev, &tpmSession, NULL, NULL, + TPM_SE_HMAC, paramEncAlg); + } if (rc != 0) goto exit; printf("TPM2_StartAuthSession: sessionHandle 0x%x\n", (word32)tpmSession.handle.hndl); @@ -246,6 +289,9 @@ int TPM2_NVRAM_Store_Example(void* userCtx, int argc, char *argv[]) } wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + wolfTPM2_UnloadHandle(&dev, &pqcKey.handle); +#endif wolfTPM2_Cleanup(&dev); return rc; diff --git a/examples/pcr/quote.c b/examples/pcr/quote.c index 37160cd0..433ebfd8 100644 --- a/examples/pcr/quote.c +++ b/examples/pcr/quote.c @@ -49,6 +49,12 @@ static void usage(void) printf("* filename: for saving the TPMS_ATTEST structure to a file\n"); printf("* -ecc: Use RSA or ECC for SRK/AIK\n"); printf("* -aes/xor: Use Parameter Encryption\n"); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + printf("* -mlkem[=512|768|1024]: Use an ML-KEM key as the param-enc " + "session salt (v1.85, default 768)\n"); + printf("* -mldsa[=44|65|87]: Use an ML-DSA key as the param-enc " + "session bind (v1.85, default 65)\n"); +#endif printf("Demo usage without parameters, generates quote over PCR%d and\n" "saves the output TPMS_ATTEST structure to \"quote.blob\" file.\n", TPM2_TEST_PCR); @@ -80,6 +86,11 @@ int TPM2_PCR_Quote_Test(void* userCtx, int argc, char *argv[]) } cmdOut; TPM_ALG_ID paramEncAlg = TPM_ALG_NULL; WOLFTPM2_SESSION tpmSession; +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + TPM_ALG_ID pqcParamEncAlg = TPM_ALG_NULL; + int pqcParamSet = 0; + WOLFTPM2_KEY pqcKey; +#endif #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) XFILE f; #endif @@ -87,6 +98,9 @@ int TPM2_PCR_Quote_Test(void* userCtx, int argc, char *argv[]) XMEMSET(&storage, 0, sizeof(storage)); XMEMSET(&aik, 0, sizeof(aik)); XMEMSET(&tpmSession, 0, sizeof(tpmSession)); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + XMEMSET(&pqcKey, 0, sizeof(pqcKey)); +#endif if (argc >= 2) { if (XSTRCMP(argv[1], "-?") == 0 || @@ -123,6 +137,24 @@ int TPM2_PCR_Quote_Test(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-xor") == 0) { paramEncAlg = TPM_ALG_XOR; } +#ifdef WOLFTPM_MLKEM + else if (XSTRCMP(argv[argc-1], "-mlkem") == 0 || + XSTRNCMP(argv[argc-1], "-mlkem=", 7) == 0) { + int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 768; + pqcParamEncAlg = TPM_ALG_MLKEM; + pqcParamSet = (n == 512) ? TPM_MLKEM_512 : + (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; + } +#endif +#ifdef WOLFTPM_MLDSA + else if (XSTRCMP(argv[argc-1], "-mldsa") == 0 || + XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) { + int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 65; + pqcParamEncAlg = TPM_ALG_MLDSA; + pqcParamSet = (n == 44) ? TPM_MLDSA_44 : + (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; + } +#endif argc--; } @@ -198,9 +230,20 @@ int TPM2_PCR_Quote_Test(void* userCtx, int argc, char *argv[]) if (alg == TPM_ALG_RSA) bindKey = NULL; /* cannot bind to key without RSA enabled */ #endif - /* Start an authenticated session (salted / unbound) with parameter encryption */ - rc = wolfTPM2_StartSession(&dev, &tpmSession, bindKey, NULL, - TPM_SE_HMAC, paramEncAlg); + #if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + if (pqcParamEncAlg != TPM_ALG_NULL) { + /* Use a PQC primary as the param-enc session key: ML-KEM salt + * or ML-DSA bind. The RSA/ECC SRK stays the AIK parent. */ + rc = getPrimaryParamEncKey(&dev, &tpmSession, &pqcKey, + pqcParamEncAlg, pqcParamSet, paramEncAlg); + } + else + #endif + { + /* Start an authenticated session (salted / unbound) with parameter encryption */ + rc = wolfTPM2_StartSession(&dev, &tpmSession, bindKey, NULL, + TPM_SE_HMAC, paramEncAlg); + } if (rc != 0) goto exit; printf("TPM2_StartAuthSession: sessionHandle 0x%x\n", (word32)tpmSession.handle.hndl); @@ -334,6 +377,9 @@ int TPM2_PCR_Quote_Test(void* userCtx, int argc, char *argv[]) wolfTPM2_UnloadHandle(&dev, &aik.handle); wolfTPM2_UnloadHandle(&dev, &storage.handle); wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + wolfTPM2_UnloadHandle(&dev, &pqcKey.handle); +#endif wolfTPM2_Cleanup(&dev); diff --git a/examples/run_examples.sh b/examples/run_examples.sh index 00ef0d0c..9a6ccb3b 100755 --- a/examples/run_examples.sh +++ b/examples/run_examples.sh @@ -332,6 +332,39 @@ if [ $ENABLE_V185 -eq 1 ]; then [ $RESULT -ne 0 ] && echo -e "mlkem_encap mlkem=$PS failed! $RESULT" && exit 1 done + echo -e "PQC primary key (create_primary -mldsa)" + for PS in 44 65 87; do + ./examples/keygen/create_primary -mldsa=$PS -oh >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "create_primary mldsa=$PS failed! $RESULT" && exit 1 + done + + echo -e "PQC parameter encryption (ML-KEM salt / ML-DSA bind)" + # ML-KEM as the param-enc session salt, ML-DSA as the param-enc session + # bind; exercise AES-CFB and XOR across child-create, attestation and NV. + ./examples/wrap/wrap_test -aes -mlkem=768 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "wrap_test -aes -mlkem failed! $RESULT" && exit 1 + ./examples/wrap/wrap_test -xor -mldsa=65 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "wrap_test -xor -mldsa failed! $RESULT" && exit 1 + ./examples/pcr/quote 16 quote.blob -ecc -aes -mlkem=768 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "quote -aes -mlkem failed! $RESULT" && exit 1 + ./examples/pcr/quote 16 quote.blob -ecc -xor -mldsa=65 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "quote -xor -mldsa failed! $RESULT" && exit 1 + ./examples/nvram/counter -aes -mlkem=768 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "counter -aes -mlkem failed! $RESULT" && exit 1 + ./examples/keygen/keygen pqcpe.bin -ecc -aes -paramkey=mlkem=768 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "keygen -paramkey=mlkem failed! $RESULT" && exit 1 + ./examples/keygen/keygen pqcpe.bin -ecc -xor -paramkey=mldsa=65 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "keygen -paramkey=mldsa failed! $RESULT" && exit 1 + rm -f pqcpe.bin quote.blob + echo -e "PQC negative verify (mldsa_verify_neg)" for PS in 44 65 87; do ./examples/pqc/mldsa_verify_neg -mldsa=$PS >> $TPMPWD/run.out 2>&1 diff --git a/examples/tpm_test_keys.c b/examples/tpm_test_keys.c index 3988e1b2..6e15eadf 100644 --- a/examples/tpm_test_keys.c +++ b/examples/tpm_test_keys.c @@ -418,6 +418,92 @@ int getPrimaryStoragekey(WOLFTPM2_DEV* pDev, WOLFTPM2_KEY* pStorageKey, return rc; } +#if defined(WOLFTPM_MLDSA) || defined(WOLFTPM_MLKEM) +int getPrimaryParamEncKey(WOLFTPM2_DEV* pDev, WOLFTPM2_SESSION* session, + WOLFTPM2_KEY* pqcKey, TPM_ALG_ID pqcAlg, int paramSet, int paramEncAlg) +{ + int rc; + TPMT_PUBLIC publicTemplate; + /* Auth value for the PQC key; gives the bound (ML-DSA) session extra + * entropy. Not used for the ML-KEM salt path. */ + const char* pqcAuth = "pqcParamEnc"; + + if (pDev == NULL || session == NULL || pqcKey == NULL) + return BAD_FUNC_ARG; + + XMEMSET(pqcKey, 0, sizeof(*pqcKey)); + XMEMSET(&publicTemplate, 0, sizeof(publicTemplate)); + + if (pqcAlg == TPM_ALG_MLKEM) { + #ifdef WOLFTPM_MLKEM + /* ML-KEM is decrypt-capable; a restricted decryption key is required + * to salt an authorization session (Part 1 Salted Session). A + * restricted decryption key must carry a symmetric definition, so set + * AES-CFB (GetKeyTemplate_MLKEM defaults symmetric to NULL, which a + * real TPM rejects with TPM_RC_SYMMETRIC for a restricted key). */ + rc = wolfTPM2_GetKeyTemplate_MLKEM(&publicTemplate, + TPMA_OBJECT_decrypt | TPMA_OBJECT_restricted | + TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent | + TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth | + TPMA_OBJECT_noDA, (TPMI_MLKEM_PARAMETER_SET)paramSet); + if (rc == TPM_RC_SUCCESS) { + publicTemplate.parameters.mlkemDetail.symmetric.algorithm = + TPM_ALG_AES; + publicTemplate.parameters.mlkemDetail.symmetric.keyBits.aes = 128; + publicTemplate.parameters.mlkemDetail.symmetric.mode.aes = + TPM_ALG_CFB; + } + #else + (void)paramSet; + return NOT_COMPILED_IN; + #endif + } + else if (pqcAlg == TPM_ALG_MLDSA) { + #ifdef WOLFTPM_MLDSA + /* ML-DSA is sign-only; it can only be a session bind entity. */ + rc = wolfTPM2_GetKeyTemplate_MLDSA(&publicTemplate, + TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | + TPMA_OBJECT_fixedParent | TPMA_OBJECT_sensitiveDataOrigin | + TPMA_OBJECT_userWithAuth | TPMA_OBJECT_noDA, + (TPMI_MLDSA_PARAMETER_SET)paramSet, 0); + #else + (void)paramSet; + return NOT_COMPILED_IN; + #endif + } + else { + return BAD_FUNC_ARG; + } + if (rc != TPM_RC_SUCCESS) + return rc; + + rc = wolfTPM2_CreatePrimaryKey(pDev, pqcKey, TPM_RH_OWNER, &publicTemplate, + (const byte*)pqcAuth, (int)XSTRLEN(pqcAuth)); + if (rc != TPM_RC_SUCCESS) + return rc; + printf("PQC param-enc key: %s primary 0x%x\n", TPM2_GetAlgName(pqcAlg), + (word32)pqcKey->handle.hndl); + + /* ML-KEM -> session SALT (tpmKey, 3rd arg); ML-DSA -> session BIND + * (4th arg, unsalted since a sign-only key cannot exchange a salt). */ + if (pqcAlg == TPM_ALG_MLKEM) { + rc = wolfTPM2_StartSession(pDev, session, pqcKey, NULL, + TPM_SE_HMAC, paramEncAlg); + } + else { + rc = wolfTPM2_StartSession(pDev, session, NULL, &pqcKey->handle, + TPM_SE_HMAC, paramEncAlg); + } + if (rc != TPM_RC_SUCCESS) { + printf("PQC param-enc StartSession failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + wolfTPM2_UnloadHandle(pDev, &pqcKey->handle); + XMEMSET(pqcKey, 0, sizeof(*pqcKey)); + } + return rc; +} +#endif /* WOLFTPM_MLDSA || WOLFTPM_MLKEM */ + int getRSAkey(WOLFTPM2_DEV* pDev, WOLFTPM2_KEY* pStorageKey, WOLFTPM2_KEY* key, void* pWolfRsaKey, int tpmDevId, const byte* auth, int authSz, TPMT_PUBLIC* publicTemplate) diff --git a/examples/tpm_test_keys.h b/examples/tpm_test_keys.h index edd11f7a..99153d9a 100644 --- a/examples/tpm_test_keys.h +++ b/examples/tpm_test_keys.h @@ -52,6 +52,21 @@ WOLFTPM_LOCAL int getPrimaryStoragekey(WOLFTPM2_DEV* pDev, WOLFTPM2_KEY* pStorageKey, TPM_ALG_ID alg); +#if defined(WOLFTPM_MLDSA) || defined(WOLFTPM_MLKEM) +/* Create a transient post-quantum primary (ML-KEM or ML-DSA) and start an + * HMAC parameter-encryption session that uses it: ML-KEM as the session + * salt key (decrypt-capable), ML-DSA as the bind key (sign-only). On + * success the session is started but not yet assigned a slot; the caller + * calls wolfTPM2_SetAuthSession() and later unloads both pqcKey->handle + * and session->handle. pqcAlg is TPM_ALG_MLKEM or TPM_ALG_MLDSA. */ +WOLFTPM_LOCAL int getPrimaryParamEncKey(WOLFTPM2_DEV* pDev, + WOLFTPM2_SESSION* session, + WOLFTPM2_KEY* pqcKey, + TPM_ALG_ID pqcAlg, + int paramSet, + int paramEncAlg); +#endif + WOLFTPM_LOCAL int getRSAkey(WOLFTPM2_DEV* pDev, WOLFTPM2_KEY* pStorageKey, WOLFTPM2_KEY* key, diff --git a/examples/wrap/include.am b/examples/wrap/include.am index bc517449..4f244862 100644 --- a/examples/wrap/include.am +++ b/examples/wrap/include.am @@ -7,7 +7,8 @@ noinst_PROGRAMS += examples/wrap/wrap_test \ examples/wrap/hmac noinst_HEADERS += examples/wrap/wrap_test.h -examples_wrap_wrap_test_SOURCES = examples/wrap/wrap_test.c +examples_wrap_wrap_test_SOURCES = examples/wrap/wrap_test.c \ + examples/tpm_test_keys.c examples_wrap_wrap_test_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) examples_wrap_wrap_test_DEPENDENCIES = src/libwolftpm.la diff --git a/examples/wrap/wrap_test.c b/examples/wrap/wrap_test.c index f1295475..02fc86d3 100644 --- a/examples/wrap/wrap_test.c +++ b/examples/wrap/wrap_test.c @@ -35,6 +35,7 @@ #include #include +#include #include /* Configuration */ @@ -50,9 +51,14 @@ static void usage(void) { printf("Expected Usage:\n"); - printf("./examples/wrap/wrap_test [-aes/xor]\n"); + printf("./examples/wrap/wrap_test [-aes/xor] [-mlkem/-mldsa]\n"); printf("* -aes/xor: Use Parameter Encryption\n"); - +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + printf("* -mlkem[=512|768|1024]: Use an ML-KEM key as the param-enc " + "session salt (v1.85, default 768)\n"); + printf("* -mldsa[=44|65|87]: Use an ML-DSA key as the param-enc " + "session bind (v1.85, default 65)\n"); +#endif } int TPM2_Wrapper_Test(void* userCtx) @@ -125,6 +131,14 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) #endif /* !WOLFTPM2_NO_WOLFCRYPT */ TPM_ALG_ID paramEncAlg = TPM_ALG_NULL; WOLFTPM2_SESSION tpmSession; +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + /* Optional PQC key used as the parameter-encryption session salt + * (ML-KEM) or bind (ML-DSA) key. */ + TPM_ALG_ID pqcParamEncAlg = TPM_ALG_NULL; + int pqcParamSet = 0; + WOLFTPM2_KEY pqcKey; + XMEMSET(&pqcKey, 0, sizeof(pqcKey)); +#endif XMEMSET(&rsaKey, 0, sizeof(rsaKey)); XMEMSET(&eccKey, 0, sizeof(eccKey)); @@ -159,6 +173,24 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-xor") == 0) { paramEncAlg = TPM_ALG_XOR; } +#ifdef WOLFTPM_MLKEM + else if (XSTRCMP(argv[argc-1], "-mlkem") == 0 || + XSTRNCMP(argv[argc-1], "-mlkem=", 7) == 0) { + int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 768; + pqcParamEncAlg = TPM_ALG_MLKEM; + pqcParamSet = (n == 512) ? TPM_MLKEM_512 : + (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; + } +#endif +#ifdef WOLFTPM_MLDSA + else if (XSTRCMP(argv[argc-1], "-mldsa") == 0 || + XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) { + int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 65; + pqcParamEncAlg = TPM_ALG_MLDSA; + pqcParamSet = (n == 44) ? TPM_MLDSA_44 : + (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; + } +#endif else { printf("Warning: Unrecognized option: %s\n", argv[argc-1]); } @@ -265,8 +297,19 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) #ifdef NO_RSA bindKey = NULL; /* cannot bind to key without RSA enabled */ #endif - rc = wolfTPM2_StartSession(&dev, &tpmSession, bindKey, NULL, - TPM_SE_HMAC, paramEncAlg); + #if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + if (pqcParamEncAlg != TPM_ALG_NULL) { + /* Use a PQC primary as the param-enc session key: ML-KEM salt + * or ML-DSA bind. The RSA storageKey stays the child parent. */ + rc = getPrimaryParamEncKey(&dev, &tpmSession, &pqcKey, + pqcParamEncAlg, pqcParamSet, paramEncAlg); + } + else + #endif + { + rc = wolfTPM2_StartSession(&dev, &tpmSession, bindKey, NULL, + TPM_SE_HMAC, paramEncAlg); + } if (rc != 0) goto exit; printf("TPM2_StartAuthSession: sessionHandle 0x%x\n", (word32)tpmSession.handle.hndl); @@ -1036,6 +1079,9 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) wolfTPM2_UnloadHandle(&dev, &eccKey.handle); wolfTPM2_UnloadHandle(&dev, &ekKey.handle); wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + wolfTPM2_UnloadHandle(&dev, &pqcKey.handle); +#endif /* Only doShutdown=1: Just shutdown the TPM */ wolfTPM2_Reset(&dev, 1, 0); From 5fe72f778902d8f06ca045fee406a4d8a73a72b4 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 18 Jun 2026 10:18:08 -0700 Subject: [PATCH 5/9] Document PQC parameter encryption options in example READMEs --- examples/README.md | 13 +++++++++++++ examples/pqc/README.md | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/examples/README.md b/examples/README.md index cc3601b2..bbfc138c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,6 +8,8 @@ The PKCS #7 and TLS examples require generating CSR's and signing them using a t To enable parameter encryption use `-aes` for AES-CFB mode or `-xor` for XOR mode. Only some TPM commands / responses support parameter encryption. If the TPM2_ API has .flags `CMD_FLAG_ENC2` or `CMD_FLAG_DEC2` set then the command will use parameter encryption / decryption. +On a v1.85 PQC capable TPM the parameter encryption session can also be keyed with a post-quantum primary: pass `-mlkem` to salt the session with an ML-KEM key or `-mldsa` to bind it to an ML-DSA key (`keygen` uses `-paramkey=mlkem|mldsa` since `-mlkem`/`-mldsa` there select the child key). See "Parameter Encryption" below. + There are some vendor specific examples, like the TPM 2.0 extra GPIO examples for ST33 and NPCT75x. ## Native API Test @@ -80,6 +82,17 @@ This behavior depends on the `sessionAttributes`: Either one can be set separately or both can be set in one authorization session. This is up to the user (developer). +### Post-quantum session keys (v1.85) + +The parameter encryption session can be keyed with a post-quantum primary instead of an RSA/ECC storage key. ML-KEM is decrypt capable and is used as the session salt key; ML-DSA is sign only and is used as the session bind key. The RSA/ECC storage key (where one is needed, such as the parent of a created child) is unchanged. `wrap_test`, `pcr/quote`, and `nvram/store` and `nvram/counter` accept `-mlkem[=512|768|1024]` and `-mldsa[=44|65|87]`; `keygen` uses `-paramkey=mlkem[=...]` / `-paramkey=mldsa[=...]` because `-mlkem`/`-mldsa` there select the child key. This requires a v1.85 PQC capable TPM. + +```sh +./examples/wrap/wrap_test -aes -mlkem=768 +./examples/pcr/quote 16 quote.blob -ecc -xor -mldsa=65 +./examples/nvram/counter -aes -mldsa=65 +./examples/keygen/keygen keyblob.bin -ecc -aes -paramkey=mlkem=768 +``` + ## CSR Generates a Certificate Signing Request for building a certificate based on a TPM key pair. diff --git a/examples/pqc/README.md b/examples/pqc/README.md index 9d41680a..16d3a605 100644 --- a/examples/pqc/README.md +++ b/examples/pqc/README.md @@ -126,3 +126,37 @@ by loading it back: A successful load prints `Loaded key to 0x80000000`. The full 18-way matrix (three variants x three parameter sets) is exercised by `examples/run_examples.sh` when v1.85 is detected in `config.h`. + +### PQC keys for parameter encryption + +A post-quantum primary can key a TPM 2.0 parameter-encryption session: +ML-KEM (decrypt capable) is used as the session salt key and ML-DSA +(sign only) as the session bind key. The session protects the command's +first sized parameter the same way an RSA/ECC salted session does. Any +RSA/ECC storage key the example needs (for example the parent of a +created child) is unchanged. + +`wrap_test`, `pcr/quote`, and `nvram/store` and `nvram/counter` take +`-mlkem[=512|768|1024]` and `-mldsa[=44|65|87]`. `keygen` uses +`-paramkey=mlkem[=...]` / `-paramkey=mldsa[=...]` because its `-mlkem` / +`-mldsa` already select the child key algorithm. + +``` +./examples/wrap/wrap_test -aes -mlkem=768 +./examples/pcr/quote 16 quote.blob -ecc -xor -mldsa=65 +./examples/nvram/counter -aes -mldsa=65 +./examples/keygen/keygen keyblob.bin -ecc -aes -paramkey=mlkem=768 +``` + +ML-KEM is a restricted decryption (salt) key, which requires a symmetric +definition; the example helper sets AES-128-CFB on it (a TPM rejects a +restricted key with no symmetric algorithm via `TPM_RC_SYMMETRIC`). + +### `create_primary` ML-DSA primary + +`examples/keygen/create_primary` can create an ML-DSA primary key: + +``` +./examples/keygen/create_primary -mldsa # default MLDSA-65 +./examples/keygen/create_primary -mldsa=87 -oh +``` From 0d0137829a1fbfc2d56fee25a96f2356db1e2f56 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 18 Jun 2026 12:01:06 -0700 Subject: [PATCH 6/9] Bind param-enc key extra auth to the session's authorized entity --- src/tpm2_param_enc.c | 16 ++++++++++++++-- src/tpm2_wrap.c | 9 ++++++++- wolftpm/tpm2.h | 7 +++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/tpm2_param_enc.c b/src/tpm2_param_enc.c index 0c94e87a..074eefd2 100644 --- a/src/tpm2_param_enc.c +++ b/src/tpm2_param_enc.c @@ -201,8 +201,20 @@ int TPM2_ParamEnc_AESCFB( static TPM2B_AUTH* TPM2_ParamEncBindKey(TPM2_AUTH_SESSION* session) { int digestSz = TPM2_GetHashDigestSize(session->authHash); - if (session->bind != NULL && session->name.size > 0 && - digestSz > 0 && session->auth.size <= (UINT16)digestSz) { + /* Append the bind authValue only when the session is bound AND it is + * authorizing its own bind entity (the authorized entity's Name in + * session->name matches the bind entity's Name in session->bindName) AND + * that authValue is not already folded into session->auth (size is just + * the session key). In every other case - a secondary encrypt-only + * session (name.size 0), or a session authorizing a different entity + * (whose authValue, if any, is already in session->auth, including the + * empty-auth case) - the bind authValue must not be added. */ + if (session->bind != NULL && digestSz > 0 && + session->name.size > 0 && + session->name.size == session->bindName.size && + XMEMCMP(session->name.name, session->bindName.name, + session->name.size) == 0 && + session->auth.size <= (UINT16)digestSz) { return session->bind; } return NULL; diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 204e4571..7aa6d0f3 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -1899,8 +1899,15 @@ int wolfTPM2_SetAuthSession(WOLFTPM2_DEV* dev, int index, session->policyAuth = tpmSession->handle.policyAuth; session->policyPass = tpmSession->handle.policyPass; - /* Capture pointer to bind */ + /* Capture pointer to bind and the bind entity Name (set by + * wolfTPM2_StartSession when bound). Used by parameter encryption to + * tell whether the session authorizes its own bind entity. */ session->bind = tpmSession->bind; + session->bindName.size = tpmSession->handle.name.size; + if (session->bindName.size > (UINT16)sizeof(session->bindName.name)) + session->bindName.size = (UINT16)sizeof(session->bindName.name); + XMEMCPY(session->bindName.name, tpmSession->handle.name.name, + session->bindName.size); /* define the symmetric algorithm */ session->authHash = tpmSession->authHash; diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 12f81067..8758059d 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -1987,6 +1987,13 @@ typedef struct TPM2_AUTH_SESSION { unsigned int policyAuth : 1; /* if policy auth should be used */ unsigned int policyPass : 1; + + /* Name of the bind entity (set when bind != NULL). Used to tell whether an + * authorization session is authorizing its own bind entity, so the bind + * authValue is folded into the parameter-encryption key only in that case + * (TPM 2.0 Part 1: the param-enc key is the session key concatenated with + * the authValue of the entity the session authorizes). */ + TPM2B_NAME bindName; } TPM2_AUTH_SESSION; /* Macros to determine TPM 2.0 Session type */ From 3b313cd5604c1f2ec387e017fc3d45eb67fa848d Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 18 Jun 2026 12:01:06 -0700 Subject: [PATCH 7/9] Cover ML-DSA bind and store PQC parameter encryption in run_examples --- examples/run_examples.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/examples/run_examples.sh b/examples/run_examples.sh index 9a6ccb3b..04f11f68 100755 --- a/examples/run_examples.sh +++ b/examples/run_examples.sh @@ -357,6 +357,23 @@ if [ $ENABLE_V185 -eq 1 ]; then ./examples/nvram/counter -aes -mlkem=768 >> $TPMPWD/run.out 2>&1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "counter -aes -mlkem failed! $RESULT" && exit 1 + ./examples/nvram/counter -xor -mldsa=65 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "counter -xor -mldsa failed! $RESULT" && exit 1 + # store/read round-trip (uses the keyblob.bin kept from earlier), both the + # ML-DSA bind and ML-KEM salt param-enc paths; read destroys the NV index. + ./examples/nvram/store -aes -mldsa=65 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "store -aes -mldsa failed! $RESULT" && exit 1 + ./examples/nvram/read -aes >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "read -aes (after store -mldsa) failed! $RESULT" && exit 1 + ./examples/nvram/store -xor -mlkem=768 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "store -xor -mlkem failed! $RESULT" && exit 1 + ./examples/nvram/read -xor >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "read -xor (after store -mlkem) failed! $RESULT" && exit 1 ./examples/keygen/keygen pqcpe.bin -ecc -aes -paramkey=mlkem=768 >> $TPMPWD/run.out 2>&1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "keygen -paramkey=mlkem failed! $RESULT" && exit 1 From 81e5fe7255a27e13d2c10007f12d35f0b40688b4 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 18 Jun 2026 12:01:06 -0700 Subject: [PATCH 8/9] Share PQC param-enc arg parser across examples --- examples/nvram/counter.c | 20 ++++---------------- examples/nvram/store.c | 20 ++++---------------- examples/pcr/quote.c | 20 ++++---------------- examples/tpm_test_keys.c | 27 +++++++++++++++++++++++++++ examples/tpm_test_keys.h | 6 ++++++ examples/wrap/wrap_test.c | 24 +++++++----------------- 6 files changed, 52 insertions(+), 65 deletions(-) diff --git a/examples/nvram/counter.c b/examples/nvram/counter.c index 9b133578..8a0e0f20 100644 --- a/examples/nvram/counter.c +++ b/examples/nvram/counter.c @@ -118,22 +118,10 @@ int TPM2_NVRAM_Counter_Example(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-xor") == 0) { paramEncAlg = TPM_ALG_XOR; } -#ifdef WOLFTPM_MLKEM - else if (XSTRCMP(argv[argc-1], "-mlkem") == 0 || - XSTRNCMP(argv[argc-1], "-mlkem=", 7) == 0) { - int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 768; - pqcParamEncAlg = TPM_ALG_MLKEM; - pqcParamSet = (n == 512) ? TPM_MLKEM_512 : - (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; - } -#endif -#ifdef WOLFTPM_MLDSA - else if (XSTRCMP(argv[argc-1], "-mldsa") == 0 || - XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) { - int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 65; - pqcParamEncAlg = TPM_ALG_MLDSA; - pqcParamSet = (n == 44) ? TPM_MLDSA_44 : - (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + else if (parsePqcParamEncArg(argv[argc-1], &pqcParamEncAlg, + &pqcParamSet)) { + /* PQC parameter-encryption session key selected */ } #endif else { diff --git a/examples/nvram/store.c b/examples/nvram/store.c index e49d48ec..88244bb3 100644 --- a/examples/nvram/store.c +++ b/examples/nvram/store.c @@ -132,22 +132,10 @@ int TPM2_NVRAM_Store_Example(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-pub") == 0) { partialStore = PUBLIC_PART_ONLY; } -#ifdef WOLFTPM_MLKEM - else if (XSTRCMP(argv[argc-1], "-mlkem") == 0 || - XSTRNCMP(argv[argc-1], "-mlkem=", 7) == 0) { - int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 768; - pqcParamEncAlg = TPM_ALG_MLKEM; - pqcParamSet = (n == 512) ? TPM_MLKEM_512 : - (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; - } -#endif -#ifdef WOLFTPM_MLDSA - else if (XSTRCMP(argv[argc-1], "-mldsa") == 0 || - XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) { - int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 65; - pqcParamEncAlg = TPM_ALG_MLDSA; - pqcParamSet = (n == 44) ? TPM_MLDSA_44 : - (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + else if (parsePqcParamEncArg(argv[argc-1], &pqcParamEncAlg, + &pqcParamSet)) { + /* PQC parameter-encryption session key selected */ } #endif else if (argv[argc-1][0] != '-') { diff --git a/examples/pcr/quote.c b/examples/pcr/quote.c index 433ebfd8..b273f3db 100644 --- a/examples/pcr/quote.c +++ b/examples/pcr/quote.c @@ -137,22 +137,10 @@ int TPM2_PCR_Quote_Test(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-xor") == 0) { paramEncAlg = TPM_ALG_XOR; } -#ifdef WOLFTPM_MLKEM - else if (XSTRCMP(argv[argc-1], "-mlkem") == 0 || - XSTRNCMP(argv[argc-1], "-mlkem=", 7) == 0) { - int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 768; - pqcParamEncAlg = TPM_ALG_MLKEM; - pqcParamSet = (n == 512) ? TPM_MLKEM_512 : - (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; - } -#endif -#ifdef WOLFTPM_MLDSA - else if (XSTRCMP(argv[argc-1], "-mldsa") == 0 || - XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) { - int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 65; - pqcParamEncAlg = TPM_ALG_MLDSA; - pqcParamSet = (n == 44) ? TPM_MLDSA_44 : - (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + else if (parsePqcParamEncArg(argv[argc-1], &pqcParamEncAlg, + &pqcParamSet)) { + /* PQC parameter-encryption session key selected */ } #endif argc--; diff --git a/examples/tpm_test_keys.c b/examples/tpm_test_keys.c index 6e15eadf..107926d0 100644 --- a/examples/tpm_test_keys.c +++ b/examples/tpm_test_keys.c @@ -502,6 +502,33 @@ int getPrimaryParamEncKey(WOLFTPM2_DEV* pDev, WOLFTPM2_SESSION* session, } return rc; } + +int parsePqcParamEncArg(const char* arg, TPM_ALG_ID* alg, int* paramSet) +{ + if (arg == NULL || alg == NULL || paramSet == NULL) + return 0; +#ifdef WOLFTPM_MLKEM + if (XSTRCMP(arg, "-mlkem") == 0 || + XSTRNCMP(arg, "-mlkem=", 7) == 0) { + int n = (arg[6] == '=') ? XATOI(&arg[7]) : 768; + *alg = TPM_ALG_MLKEM; + *paramSet = (n == 512) ? TPM_MLKEM_512 : + (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; + return 1; + } +#endif +#ifdef WOLFTPM_MLDSA + if (XSTRCMP(arg, "-mldsa") == 0 || + XSTRNCMP(arg, "-mldsa=", 7) == 0) { + int n = (arg[6] == '=') ? XATOI(&arg[7]) : 65; + *alg = TPM_ALG_MLDSA; + *paramSet = (n == 44) ? TPM_MLDSA_44 : + (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; + return 1; + } +#endif + return 0; +} #endif /* WOLFTPM_MLDSA || WOLFTPM_MLKEM */ int getRSAkey(WOLFTPM2_DEV* pDev, WOLFTPM2_KEY* pStorageKey, WOLFTPM2_KEY* key, diff --git a/examples/tpm_test_keys.h b/examples/tpm_test_keys.h index 99153d9a..f0b716a0 100644 --- a/examples/tpm_test_keys.h +++ b/examples/tpm_test_keys.h @@ -65,6 +65,12 @@ WOLFTPM_LOCAL int getPrimaryParamEncKey(WOLFTPM2_DEV* pDev, TPM_ALG_ID pqcAlg, int paramSet, int paramEncAlg); + +/* Parse a PQC parameter-encryption key option: "-mlkem[=512|768|1024]" + * (salt) or "-mldsa[=44|65|87]" (bind). On match sets *alg (TPM_ALG_MLKEM / + * TPM_ALG_MLDSA) and *paramSet and returns 1; returns 0 if not recognized. */ +WOLFTPM_LOCAL int parsePqcParamEncArg(const char* arg, TPM_ALG_ID* alg, + int* paramSet); #endif WOLFTPM_LOCAL int getRSAkey(WOLFTPM2_DEV* pDev, diff --git a/examples/wrap/wrap_test.c b/examples/wrap/wrap_test.c index 02fc86d3..03fff448 100644 --- a/examples/wrap/wrap_test.c +++ b/examples/wrap/wrap_test.c @@ -137,13 +137,15 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) TPM_ALG_ID pqcParamEncAlg = TPM_ALG_NULL; int pqcParamSet = 0; WOLFTPM2_KEY pqcKey; - XMEMSET(&pqcKey, 0, sizeof(pqcKey)); #endif XMEMSET(&rsaKey, 0, sizeof(rsaKey)); XMEMSET(&eccKey, 0, sizeof(eccKey)); XMEMSET(&aesKey, 0, sizeof(aesKey)); XMEMSET(&publicKey, 0, sizeof(publicKey)); +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + XMEMSET(&pqcKey, 0, sizeof(pqcKey)); +#endif #ifndef WOLFTPM2_NO_WOLFCRYPT #ifndef NO_RSA XMEMSET(&wolfRsaPubKey, 0, sizeof(wolfRsaPubKey)); @@ -173,22 +175,10 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) else if (XSTRCMP(argv[argc-1], "-xor") == 0) { paramEncAlg = TPM_ALG_XOR; } -#ifdef WOLFTPM_MLKEM - else if (XSTRCMP(argv[argc-1], "-mlkem") == 0 || - XSTRNCMP(argv[argc-1], "-mlkem=", 7) == 0) { - int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 768; - pqcParamEncAlg = TPM_ALG_MLKEM; - pqcParamSet = (n == 512) ? TPM_MLKEM_512 : - (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; - } -#endif -#ifdef WOLFTPM_MLDSA - else if (XSTRCMP(argv[argc-1], "-mldsa") == 0 || - XSTRNCMP(argv[argc-1], "-mldsa=", 7) == 0) { - int n = (argv[argc-1][6] == '=') ? XATOI(&argv[argc-1][7]) : 65; - pqcParamEncAlg = TPM_ALG_MLDSA; - pqcParamSet = (n == 44) ? TPM_MLDSA_44 : - (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; +#if defined(WOLFTPM_MLKEM) || defined(WOLFTPM_MLDSA) + else if (parsePqcParamEncArg(argv[argc-1], &pqcParamEncAlg, + &pqcParamSet)) { + /* PQC parameter-encryption session key selected */ } #endif else { From de85c26ec6ca49087317ca1c263b08ea2b66bc4e Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 19 Jun 2026 11:18:13 -0700 Subject: [PATCH 9/9] Peer review fixes --- examples/keygen/keygen.c | 23 +++--------------- examples/tpm_test_keys.c | 50 ++++++++++++++++++++++++++++++---------- examples/tpm_test_keys.h | 11 ++++++++- src/tpm2_param_enc.c | 8 ++++++- wolftpm/tpm2.h | 4 +++- 5 files changed, 61 insertions(+), 35 deletions(-) diff --git a/examples/keygen/keygen.c b/examples/keygen/keygen.c index 931317da..eb23373b 100644 --- a/examples/keygen/keygen.c +++ b/examples/keygen/keygen.c @@ -297,27 +297,10 @@ int TPM2_Keygen_Example(void* userCtx, int argc, char *argv[]) XSTRLEN("-paramkey=")) == 0) { /* Choose a PQC key for the param-enc session (separate from the * child key -mlkem/-mldsa selection above). Value is e.g. - * "mlkem", "mlkem=768", "mldsa" or "mldsa=65". */ + * "mlkem", "mlkem=768", "mldsa" or "mldsa=65". Reuses the shared + * value-form parser so validation stays consistent. */ const char* pkVal = argv[argc-1] + XSTRLEN("-paramkey="); - #ifdef WOLFTPM_MLKEM - if (XSTRNCMP(pkVal, "mlkem", 5) == 0) { - int n = (pkVal[5] == '=') ? XATOI(&pkVal[6]) : 768; - pqcParamEncAlg = TPM_ALG_MLKEM; - pqcParamSet = (n == 512) ? TPM_MLKEM_512 : - (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; - } - else - #endif - #ifdef WOLFTPM_MLDSA - if (XSTRNCMP(pkVal, "mldsa", 5) == 0) { - int n = (pkVal[5] == '=') ? XATOI(&pkVal[6]) : 65; - pqcParamEncAlg = TPM_ALG_MLDSA; - pqcParamSet = (n == 44) ? TPM_MLDSA_44 : - (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; - } - else - #endif - { + if (!parsePqcParamSet(pkVal, &pqcParamEncAlg, &pqcParamSet)) { printf("Invalid -paramkey value: %s\n", pkVal); usage(); return 0; diff --git a/examples/tpm_test_keys.c b/examples/tpm_test_keys.c index 107926d0..2cf2afda 100644 --- a/examples/tpm_test_keys.c +++ b/examples/tpm_test_keys.c @@ -503,32 +503,58 @@ int getPrimaryParamEncKey(WOLFTPM2_DEV* pDev, WOLFTPM2_SESSION* session, return rc; } -int parsePqcParamEncArg(const char* arg, TPM_ALG_ID* alg, int* paramSet) +int parsePqcParamSet(const char* val, TPM_ALG_ID* alg, int* paramSet) { - if (arg == NULL || alg == NULL || paramSet == NULL) + int n; + + if (val == NULL || alg == NULL || paramSet == NULL) return 0; #ifdef WOLFTPM_MLKEM - if (XSTRCMP(arg, "-mlkem") == 0 || - XSTRNCMP(arg, "-mlkem=", 7) == 0) { - int n = (arg[6] == '=') ? XATOI(&arg[7]) : 768; + if (XSTRCMP(val, "mlkem") == 0 || + XSTRNCMP(val, "mlkem=", 6) == 0) { + n = (val[5] == '=') ? XATOI(&val[6]) : 768; *alg = TPM_ALG_MLKEM; - *paramSet = (n == 512) ? TPM_MLKEM_512 : - (n == 1024) ? TPM_MLKEM_1024 : TPM_MLKEM_768; + if (n == 512) + *paramSet = TPM_MLKEM_512; + else if (n == 768) + *paramSet = TPM_MLKEM_768; + else if (n == 1024) + *paramSet = TPM_MLKEM_1024; + else { + printf("Invalid ML-KEM parameter set: %d (use 512, 768, or 1024)\n", + n); + return 0; + } return 1; } #endif #ifdef WOLFTPM_MLDSA - if (XSTRCMP(arg, "-mldsa") == 0 || - XSTRNCMP(arg, "-mldsa=", 7) == 0) { - int n = (arg[6] == '=') ? XATOI(&arg[7]) : 65; + if (XSTRCMP(val, "mldsa") == 0 || + XSTRNCMP(val, "mldsa=", 6) == 0) { + n = (val[5] == '=') ? XATOI(&val[6]) : 65; *alg = TPM_ALG_MLDSA; - *paramSet = (n == 44) ? TPM_MLDSA_44 : - (n == 87) ? TPM_MLDSA_87 : TPM_MLDSA_65; + if (n == 44) + *paramSet = TPM_MLDSA_44; + else if (n == 65) + *paramSet = TPM_MLDSA_65; + else if (n == 87) + *paramSet = TPM_MLDSA_87; + else { + printf("Invalid ML-DSA parameter set: %d (use 44, 65, or 87)\n", n); + return 0; + } return 1; } #endif return 0; } + +int parsePqcParamEncArg(const char* arg, TPM_ALG_ID* alg, int* paramSet) +{ + if (arg == NULL || arg[0] != '-') + return 0; + return parsePqcParamSet(arg + 1, alg, paramSet); +} #endif /* WOLFTPM_MLDSA || WOLFTPM_MLKEM */ int getRSAkey(WOLFTPM2_DEV* pDev, WOLFTPM2_KEY* pStorageKey, WOLFTPM2_KEY* key, diff --git a/examples/tpm_test_keys.h b/examples/tpm_test_keys.h index f0b716a0..4da4adc5 100644 --- a/examples/tpm_test_keys.h +++ b/examples/tpm_test_keys.h @@ -66,9 +66,18 @@ WOLFTPM_LOCAL int getPrimaryParamEncKey(WOLFTPM2_DEV* pDev, int paramSet, int paramEncAlg); +/* Parse a PQC parameter-set value (no leading dash): "mlkem[=512|768|1024]" + * or "mldsa[=44|65|87]". On match sets *alg (TPM_ALG_MLKEM / TPM_ALG_MLDSA) + * and *paramSet and returns 1. Returns 0 if the value is not recognized, or + * if a parameter set is given but is not a supported value (a message is + * printed in that case). */ +WOLFTPM_LOCAL int parsePqcParamSet(const char* val, TPM_ALG_ID* alg, + int* paramSet); + /* Parse a PQC parameter-encryption key option: "-mlkem[=512|768|1024]" * (salt) or "-mldsa[=44|65|87]" (bind). On match sets *alg (TPM_ALG_MLKEM / - * TPM_ALG_MLDSA) and *paramSet and returns 1; returns 0 if not recognized. */ + * TPM_ALG_MLDSA) and *paramSet and returns 1; returns 0 if not recognized or + * if an unsupported parameter set is given. Wraps parsePqcParamSet(). */ WOLFTPM_LOCAL int parsePqcParamEncArg(const char* arg, TPM_ALG_ID* alg, int* paramSet); #endif diff --git a/src/tpm2_param_enc.c b/src/tpm2_param_enc.c index 074eefd2..6da01a89 100644 --- a/src/tpm2_param_enc.c +++ b/src/tpm2_param_enc.c @@ -208,7 +208,13 @@ static TPM2B_AUTH* TPM2_ParamEncBindKey(TPM2_AUTH_SESSION* session) * the session key). In every other case - a secondary encrypt-only * session (name.size 0), or a session authorizing a different entity * (whose authValue, if any, is already in session->auth, including the - * empty-auth case) - the bind authValue must not be added. */ + * empty-auth case) - the bind authValue must not be added. + * The size test uses <= (not ==) deliberately: in the realistic case the + * lone session key is exactly one digest, but <= is kept as a defensive + * lower bound so a bound-own-entity session is still admitted even if the + * session key is shorter than a full digest. A session authorizing a + * different entity always has auth.size strictly greater than the digest + * (sessionKey || entityAuth) and so remains excluded either way. */ if (session->bind != NULL && digestSz > 0 && session->name.size > 0 && session->name.size == session->bindName.size && diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 8758059d..ba679d5b 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -1992,7 +1992,9 @@ typedef struct TPM2_AUTH_SESSION { * authorization session is authorizing its own bind entity, so the bind * authValue is folded into the parameter-encryption key only in that case * (TPM 2.0 Part 1: the param-enc key is the session key concatenated with - * the authValue of the entity the session authorizes). */ + * the authValue of the entity the session authorizes). + * Placed at the end of the struct so the offsets of existing members are + * unchanged; the size growth is covered by the 4.0.0 major version. */ TPM2B_NAME bindName; } TPM2_AUTH_SESSION;