Skip to content

PQC parameter encryption for examples, plus bound-session key fixes#531

Open
dgarske wants to merge 9 commits into
wolfSSL:masterfrom
dgarske:create_primary_mldsa
Open

PQC parameter encryption for examples, plus bound-session key fixes#531
dgarske wants to merge 9 commits into
wolfSSL:masterfrom
dgarske:create_primary_mldsa

Conversation

@dgarske

@dgarske dgarske commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

Adds the ability to use a post-quantum key for TPM 2.0 parameter encryption (ML-KEM as the session salt key, ML-DSA as the session bind key) across several examples, plus create_primary ML-DSA support. Along the way it fixes two wolfTPM session-key derivation bugs (client and firmware TPM) that real TPMs reject.

Details

Fix 1 - bound session parameter encryption key: a session bound to one entity but used to encrypt a command that authorizes a different entity (or used as a secondary encrypt-only session) wrongly concatenated the bound entity's authValue to the parameter-encryption key. Per TPM 2.0 the key is the session key concatenated with the authValue of the entity the encrypt/decrypt session authorizes; the bind authValue is only added when the session authorizes its own bind entity. The symptom was TPM_RC_SIZE on commands with a structured encrypted parameter (such as Create), rejected by both the wolfTPM firmware TPM and the TCG reference TPM.

Fix 2 - bound EmptyAuth session key: both the client (wolfTPM2_StartSession) and the firmware TPM (StartAuthSession) left the session key empty when a session was bound to an entity whose authValue is the Empty Buffer, instead of still computing KDFa over a zero-length key input as the specification requires. Fixed on both sides.

Feature: new options that create a transient PQC primary and use it as the parameter-encryption session salt (ML-KEM, decrypt-capable) or bind (ML-DSA, sign-only) key, wired in via a shared helper. wrap_test, pcr/quote, and nvram store and counter take -mlkem[=512|768|1024] and -mldsa[=44|65|87]; keygen takes -paramkey=mlkem|mldsa (its -mlkem/-mldsa already select the child key). create_primary gains -mldsa[=44|65|87] to create an ML-DSA primary. run_examples.sh gains coverage for all of the above.

Test matrix

Verified on three TPMs: the wolfSSL firmware TPM (fwTPM), the IBM TCG reference TPM (ibmswtpm2), and a real ST v1.85 PQC TPM over SPI.

  • ML-KEM salt and ML-DSA bind parameter encryption, in both AES-CFB and XOR modes, across child-key creation (keygen, wrap_test), attestation (pcr/quote), and NV (nvram/counter): pass on fwTPM and the real ST TPM.
  • Bound-session and EmptyAuth fixes: a CreateKey reproducer using a bound session as a secondary encrypt session (ECC, RSA, and ML-DSA bind keys) previously returned TPM_RC_SIZE and now succeeds on fwTPM and the IBM reference TPM; a bound EmptyAuth session likewise now round-trips on fwTPM and the IBM reference.
  • create_primary -mldsa (parameter sets 44, 65, 87) on fwTPM and the real ST TPM.
  • fwTPM unit tests, client unit tests, and run_examples.sh: pass.
  • ML-KEM is used as a restricted decryption salt key, which requires a symmetric definition; the helper sets AES-128-CFB (a real TPM returns TPM_RC_SYMMETRIC without it, though fwTPM was lenient).

@dgarske dgarske self-assigned this Jun 18, 2026
Copilot AI review requested due to automatic review settings June 18, 2026 17:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates wolfTPM’s session and parameter-encryption behavior to support PQC-keyed parameter-encryption sessions in examples (ML-KEM as salt key, ML-DSA as bind key), adds create_primary ML-DSA support, and fixes two spec-compliance issues in session-key/param-key derivation that caused real TPMs to reject commands.

Changes:

  • Fix sessionKey derivation for bound sessions with EmptyAuth (client and fwTPM): sessionKey is now computed even with zero-length bind authValue input, per TPM 2.0.
  • Fix parameter-encryption key derivation for bound sessions by avoiding unconditional concatenation of the bind authValue.
  • Add PQC parameter-encryption options (-mlkem, -mldsa, -paramkey=...) across multiple examples; add create_primary -mldsa; extend docs and run_examples.sh coverage.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/tpm2_wrap.c Adjusts client sessionKey derivation to compute sessionKey when bound even if bind authValue is empty.
src/tpm2_param_enc.c Changes param-encryption key building to only append bind authValue in specific cases (instead of always).
src/fwtpm/fwtpm_command.c Mirrors bound/EmptyAuth sessionKey derivation fix in fwTPM StartAuthSession implementation.
examples/wrap/wrap_test.c Adds PQC param-encryption CLI options and uses shared helper to create PQC primary + start session.
examples/wrap/include.am Links wrap_test against examples/tpm_test_keys.c for the new helper.
examples/tpm_test_keys.h Declares new helper for PQC primary + param-encryption session setup.
examples/tpm_test_keys.c Implements PQC primary creation and session start for ML-KEM (salt) / ML-DSA (bind).
examples/run_examples.sh Adds v1.85 PQC runs for create_primary -mldsa and PQC-keyed parameter encryption coverage.
examples/README.md Documents PQC-keyed parameter-encryption session options and usage.
examples/pqc/README.md Adds PQC parameter-encryption documentation and create_primary -mldsa notes.
examples/pcr/quote.c Adds PQC param-encryption CLI options and uses PQC helper.
examples/nvram/store.c Adds PQC param-encryption CLI options and uses PQC helper.
examples/nvram/counter.c Adds PQC param-encryption CLI options and uses PQC helper.
examples/keygen/keygen.c Adds `-paramkey=mlkem
examples/keygen/create_primary.c Adds `-mldsa[=44

Comment thread src/tpm2_param_enc.c
@dgarske dgarske requested a review from Copilot June 18, 2026 20:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

examples/tpm_test_keys.c:1

  • Invalid parameter-set values are silently coerced to the default (e.g., -mlkem=999 becomes 768; -mldsa=1 becomes 65). This makes CLI behavior non-obvious and can mask user errors. Recommend validating the numeric value and returning 0 (unrecognized) or a dedicated error path so callers can print a clear message and usage() when an unsupported parameter set is provided.
/* tpm_test_keys.c

Comment thread src/tpm2_param_enc.c
Comment on lines +212 to +219
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;
}
Comment thread wolftpm/tpm2.h
Comment on lines +1991 to +1996
/* 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;
Comment thread examples/keygen/keygen.c
Comment on lines +296 to +325
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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants