Skip to content

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

Merged
aidangarske merged 6 commits into
wolfSSL:masterfrom
dgarske:create_primary_mldsa
Jun 29, 2026
Merged

PQC parameter encryption for examples, plus bound-session key fixes#531
aidangarske merged 6 commits into
wolfSSL:masterfrom
dgarske:create_primary_mldsa

Conversation

@dgarske

@dgarske dgarske commented Jun 18, 2026

Copy link
Copy Markdown
Member

PQC parameter encryption for examples, plus parameter-encryption fixes

Adds post-quantum (ML-DSA / ML-KEM) parameter-encryption support to the examples, and fixes several parameter-encryption bugs in the client and the firmware TPM that were uncovered along the way.

Features

  • PQC-keyed parameter-encryption sessions: -mlkem[=512|768|1024] (salt key) and -mldsa[=44|65|87] (bind key), plus -paramkey= in keygen. Wired through keygen, wrap_test, pcr/quote, nvram/store and nvram/counter, with a shared parser in tpm_test_keys. ML-DSA is sign-only, so the helper adds a transient SRK salt for confidentiality while the ML-DSA key supplies the binding.
  • create_primary gains -mldsa[=44|65|87] for an ML-DSA primary.
  • Selecting a PQC param-enc key without -aes/-xor now defaults the session cipher to AES-CFB instead of silently doing nothing.

Fixes

  • Bound-session session-key derivation: compute KDFa(bindAuth || salt) (TPM 2.0 Part 1 Sec.19.6.8) on both the client and the firmware TPM. The firmware TPM had the inputs reversed (salt || bindAuth), which broke salt+bind sessions, and a session bound to an EmptyAuth entity previously left the session key empty.
  • Parameter-encryption key authValue: the param-enc key is sessionKey || authValue even when the session is bound to the entity it authorizes (unlike the command HMAC, which excludes it). The firmware TPM omitted the authValue and silently corrupted parameter-encrypted data for bound-own-entity flows (e.g. examples/nvram/extend.c); now fixed on both sides (TPM2_ParamEncBindKey / FwBuildParamEncKey, with bindName on TPM2_AUTH_SESSION tracking the bind entity).
  • TPM2_CreateLoaded: set the response outHandleCnt, which was mis-locating the response parameters (and rpHash) and rejecting the reply with TPM_RC_HMAC whenever it ran under a parameter-encryption session.
  • PQC option parsing: alg/parameter-set outputs are committed only after validation, and an unsupported set (e.g. -mlkem=999) is now a fatal usage error in every example instead of silently dropping parameter encryption.
  • Single-family builds: the -mlkem and -mldsa help lines are each guarded by their own family macro, so an ML-KEM-only or ML-DSA-only build only advertises what it supports.

Tests / testing

  • New unit.test regressions: bound-EmptyAuth param-enc; TPM2_CreateLoaded under param-enc (fails pre-fix with TPM_RC_HMAC); and a bound-own-entity param-enc test that writes a POLICYWRITE NV index under a bound policy session and reads the data back, proving the param-enc key is correct (catches a wrong key on either the client or the firmware TPM).
  • run_examples.sh exercises the ML-KEM-salt and ML-DSA-bind param-enc paths across child-create, attestation and NV.
  • Verified against both the firmware TPM and the IBM SW TPM (reference); ML-DSA-only and ML-KEM-only builds link cleanly.

@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 thread wolftpm/tpm2.h Outdated
Comment thread examples/keygen/keygen.c

@aidangarske aidangarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Skoll Multi-Scan Review

Modes: review + review-securityOverall recommendation: COMMENT
Findings: 6 total — 4 posted, 2 skipped
2 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [Medium] [review+review-security] create_primary ML-DSA support guarded by WOLFTPM_PQC but links against the WOLFTPM_MLDSA-only symbol wolfTPM2_GetKeyTemplate_MLDSAexamples/keygen/create_primary.c:90-102,142-154,262-270
  • [Medium] [review] No automated regression test added for the bound-session / EmptyAuth-bind session-key fixessrc/tpm2_param_enc.c:201-227, src/tpm2_wrap.c:2627, src/fwtpm/fwtpm_command.c:8306-8325
  • [Low] [review] parsePqcParamSet treats a trailing '=' with empty suffix as an error instead of defaultingexamples/tpm_test_keys.c:512-548
  • [Low] [review] Selecting -mlkem/-mldsa (or -paramkey=) without -aes/-xor silently does nothingexamples/nvram/counter.c:152-175, examples/nvram/store.c:172, examples/pcr/quote.c:218, examples/wrap/wrap_test.c:287, examples/keygen/keygen.c:386

Skipped findings

  • [Info] Parameter-encryption bind-key selection now depends on session-name ordering
  • [Info] TPM2_AUTH_SESSION grows by a TPM2B_NAME (bindName) — ABI/size change

Review generated by Skoll

Comment thread examples/keygen/create_primary.c Outdated
Comment thread examples/tpm_test_keys.c
@dgarske
dgarske force-pushed the create_primary_mldsa branch from de85c26 to b2faa40 Compare June 23, 2026 01:48
@dgarske
dgarske requested a review from aidangarske June 23, 2026 01:48

@aidangarske aidangarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Skoll Multi-Scan Review

Modes: review + review-security + bugsOverall recommendation: COMMENT
Findings: 4 total — 4 posted, 0 skipped
4 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [Medium] [review-security] ML-DSA bind parameter encryption is derived from a public authValueexamples/tpm_test_keys.c:427-495
  • [Medium] [review+review-security+bugs] Invalid PQC parameter-set parse leaves parser outputs armedexamples/tpm_test_keys.c:506-548
  • [Medium] [review+bugs] wrap_test PQC parameter-encryption session is overwritten before useexamples/wrap/wrap_test.c:300-319
  • [Medium] [review+review-security+bugs] Bound EmptyAuth regression test does not exercise the bound HMAC sessiontests/unit_tests.c:709-734

Review generated by Skoll

Comment thread examples/tpm_test_keys.c Outdated
Comment thread examples/tpm_test_keys.c
Comment thread examples/wrap/wrap_test.c
Comment thread tests/unit_tests.c
@aidangarske aidangarske assigned dgarske and unassigned wolfSSL-Bot Jun 23, 2026
@dgarske
dgarske force-pushed the create_primary_mldsa branch 2 times, most recently from 18fb248 to 22a1fc6 Compare June 25, 2026 18:36

@aidangarske aidangarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Skoll Multi-Scan Review

Modes: review + review-security + bugsOverall recommendation: COMMENT
Findings: 4 total — 4 posted, 0 skipped
2 finding(s) posted as inline comments (see file-level comments below)
2 finding(s) not tied to a diff line (full detail below)

Posted findings

  • [Medium] [review-security+bugs] fwTPM parameter encryption helpers can return an uninitialized statussrc/fwtpm/fwtpm_command.c:221-284
  • [Medium] [review+bugs] create_primary ML-DSA parser treats explicit invalid suffixes as the defaultexamples/keygen/create_primary.c:91-100

Findings not tied to a diff line

Invalid PQC option can be ignored after a previous valid option

File: examples/wrap/wrap_test.c:181-189; examples/nvram/counter.c:124-132; examples/nvram/store.c:138-146; examples/pcr/quote.c:143-151
Function: TPM2_Wrapper_TestArgs / TPM2_NVRAM_Counter_Example / TPM2_NVRAM_Store_Example / TPM2_PCR_Quote_Test
Severity: Medium
Category: bug

All three scan modes flagged this. The new callers use pqcParamEncAlg == TPM_ALG_NULL to decide whether parsePqcParamEncArg() reported an invalid PQC parameter set. That only works before any valid PQC option has been parsed. These examples parse argv from right to left, so a command like wrap_test -mldsa=999 -mlkem=768 first sets pqcParamEncAlg to ML-KEM, then parsePqcParamEncArg() returns -1 for -mldsa=999 without clearing the already-committed output. The invalid option is then silently accepted instead of being the fatal usage error this PR intends. Severity views differ (review: Medium; review-security and bugs: Low) — keeping the stricter Medium.

Recommendation: Store the parser return value and branch on it directly: int pqcRc = parsePqcParamEncArg(...); if (pqcRc < 0) { usage(); return -1; } else if (pqcRc > 0) { /* valid PQC option */ }. Add a regression test with one valid and one invalid PQC option in both argument orders.

Referenced code: examples/wrap/wrap_test.c:181-189; examples/nvram/counter.c:124-132; examples/nvram/store.c:138-146; examples/pcr/quote.c:143-149 (7 lines)


Zephyr wrap_test target misses the new helper source dependency

File: examples/wrap/wrap_test.c:38,181-189,311-312; examples/wrap/include.am:13-14
Function: TPM2_Wrapper_TestArgs
Severity: Medium
Category: Regression

The PR makes wrap_test.c include examples/tpm_test_keys.h and call parsePqcParamEncArg() / getPrimaryParamEncKey() under WOLFTPM_MLKEM || WOLFTPM_MLDSA. Automake and the top-level CMake helper were updated to compile/link examples/tpm_test_keys.c, but the Zephyr sample zephyr/samples/wolftpm_wrap_test/CMakeLists.txt still builds only examples/wrap/wrap_test.c. A PQC-enabled Zephyr wrap_test build will have undefined references to the new helper functions.

Recommendation: Update the Zephyr wrap_test sample to compile examples/tpm_test_keys.c when the same PQC macros can enable these calls, or move the helper linkage into shared Zephyr app sources so the sample stays aligned with the other build systems.

Referenced code: examples/wrap/wrap_test.c:38,181-189,311-312; examples/wrap/include.am:13-19 (7 lines)


Review generated by Skoll

Comment thread src/fwtpm/fwtpm_command.c
Comment thread examples/keygen/create_primary.c
@dgarske
dgarske force-pushed the create_primary_mldsa branch from 8f68f76 to 47dc71f Compare June 29, 2026 17:12
@dgarske
dgarske force-pushed the create_primary_mldsa branch from 47dc71f to ec93a9b Compare June 29, 2026 17:34
@dgarske dgarske assigned wolfSSL-Bot and unassigned dgarske Jun 29, 2026
@dgarske
dgarske requested a review from aidangarske June 29, 2026 18:29
@aidangarske
aidangarske merged commit a163f74 into wolfSSL:master Jun 29, 2026
196 checks passed
@aidangarske
aidangarske deleted the create_primary_mldsa branch June 29, 2026 19:08
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.

4 participants