Skip to content

Keep slot-1 session in SignSequenceComplete - #642

Open
deadcaf3 wants to merge 5 commits into
wolfSSL:masterfrom
deadcaf3:worktree-fix-slot-overwrite
Open

deadcaf3 wants to merge 5 commits into
wolfSSL:masterfrom
deadcaf3:worktree-fix-slot-overwrite

Conversation

@deadcaf3

Copy link
Copy Markdown
Contributor

Problem

wolfTPM2_SignSequenceComplete sets slot 1 with wolfTPM2_SetAuthHandle, which resets it to a password session. If the caller had an HMAC session there for parameter encryption, it is silently dropped. The next wolfTPM2_Decapsulate then runs without it, and the ML-KEM shared secret comes back in the clear.

Fix

Save slot 1 before the command and restore it after. If it holds an HMAC session and slot 2 is free, run that session from slot 2 during the sign so it still protects the command, then copy it back with its updated nonces.

Switching to wolfTPM2_SetAuthHandleName does not work: an unbound HMAC session does not carry the key's authValue, so signing with a password-protected key fails with TPM_RC_AUTH_FAIL.

Testing

test_wolfTPM2_MLDSA_SignSequence_NonEmptyAuth now signs with an HMAC session in slot 1, checks the session survives with a rolled nonce, and verifies the signature through it. Passes on fwTPM (--enable-v185). Fails before the fix, with the SetAuthHandleName variant (0x8e), and without the slot-2 move.

wolfTPM2_SignSequenceComplete authorizes keyHandle with
wolfTPM2_SetAuthHandle(dev, 1, ...), which resets session slot 1 to
password auth. A caller's parameter-encryption HMAC session in slot 1
was silently dropped, so the next command (e.g. wolfTPM2_Decapsulate)
returned its secret unencrypted and the session's rolling nonce was
lost.

Save slot 1 before the command and restore it afterwards. When the
saved session is an HMAC session and slot 2 is free, run it from slot
2 for this command, where the TPM takes extra encrypt and audit
sessions, so it also protects the sign. Copy it back with the nonces
the command advanced.

Authorizing the key through the slot-1 session instead would fail
with TPM_RC_AUTH_FAIL for keys with an authValue, since an unbound
HMAC session does not carry the key's authValue.

Extend the ML-DSA key-auth sign test to sign with an HMAC session in
slot 1, check the session is kept and its nonce rolled, and verify the
signature through the same session.

@claude claude Bot 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.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

Copilot AI balanced review requested due to automatic review settings October 21, 2026 17:59

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.

🟡 Changes recommended

Single-slot memory safety, policy-session restoration, and two-slot test correctness need fixes.

3 open issues
What changed in this PR

Preserves the caller’s slot-1 session across signing so later commands retain parameter encryption.

Changes:

  • Saves and restores slot 1, temporarily using slot 2 when available.
  • Adds regression checks for session preservation, nonce updates, and signature verification.
File Description
tests/​unit_tests.c Tests signing with a slot-1 HMAC session.
src/​tpm2_wrap.c Preserves session state across SignSequenceComplete.

Review effort: Balanced

Comment thread src/tpm2_wrap.c
Comment thread src/tpm2_wrap.c Outdated
Comment thread tests/unit_tests.c Outdated
- With MAX_SESSION_NUM=1 (--enable-smallstack --disable-wolfcrypt)
  the slot-1 save and restore read and wrote past dev->session.
  Compile them only when a second slot exists.
- A policy-auth key keeps its policy session in slot 1 and the
  command advances its nonce; restoring the snapshot rolled that
  nonce back. Skip the save and restore for policyAuth keys.
- With MAX_SESSION_NUM=2 the session cannot move to slot 2, so its
  nonce does not roll. Check the rolled nonce only when slot 2
  exists.
@dgarske

dgarske commented Oct 21, 2026

Copy link
Copy Markdown
Member

Okay to test. Contributor agreement on file.

@dgarske dgarske self-assigned this Oct 21, 2026

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.

🟡 Changes recommended

Relocating bound-session authorization context can cause signing of incorrectly decrypted message bytes.

1 open issue
3 resolved since last review

Review effort: Balanced

Comment thread src/tpm2_wrap.c
The slot-1 session copied into slot 2 kept its Name. For a session
bound to that entity (name == bindName), TPM2_ParamEncBindKey() then
appended the bind authValue to the parameter-encryption key, but slot
2 authorizes no handle, so the TPM used the session key alone. The
command HMAC still validated, while the TPM decrypted the message to
other bytes and signed those.

Clear the Name in slot 2 and put the caller's Name back on restore,
unless the response retired the session. Add a bound-session
regression that verifies the signature over the original message.

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.

🔵 Needs a closer look

The regression test can abort rather than skip unsupported sequence signing; cryptographic session handling also warrants human review.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Avoid retrying ML-DSA signing when SignSequenceStart is unsupported

tests/​unit_tests.c:10570

Medium (CWE-617): If the TPM supports the ML-DSA key but rejects SignSequenceStart as unsupported, the preceding helper sets sigSz to zero and returns. This block still starts a session and retries signing. That attempt also skips without advancing the TPM nonce, so the nonce assertion fails when MAX_SESSION_NUM > 2, aborting the suite instead of preserving the existing skip behavior. Unload the key and return before starting the session when the initial helper produces no signature.

1 resolved since last review

Review effort: Balanced

When the TPM rejects SignSequenceStart, the sign helper sets sigSz to
zero and returns. The slot-1 session checks still ran, and with a
free slot 2 the nonce assertion failed because nothing advanced the
nonce, aborting the suite. Unload the key and return once the first
sign produces no signature.

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.

🔵 Needs a closer look

Security-sensitive session relocation and nonce restoration need final human validation across supported TPM configurations.

No open issues 🎉

Review effort: Balanced

@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.

Looks good! could you fix the concern below

Comment thread src/tpm2_wrap.c
* slot-1 session (e.g. parameter encryption) after the command. While
* slot 2 is free, that session also protects this command from there.
* A policy-auth key runs on the slot-1 session itself; keep it live. */
if (!key->handle.policyAuth) {

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.

The added tests only cover policyAuth==0 keys, so this skip branch never runs. Can you add a policy-authorized ML-DSA sign with the policy session in slot 1, and assert the handle survives, its nonce advances, and a later command on that session still succeeds?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, and added test_wolfTPM2_MLDSA_SignSequence_PolicyAuth in 3db79c0. It signs with a policy-only ML-DSA key using a policy session in slot 1, then checks that:

  • the session handle is still in slot 1
  • its nonceTPM advanced
  • a second PolicyAuthValue and sign on the same session succeeds

Removing the policyAuth guard makes the test fail. It also passes with MAX_SESSION_NUM=2 and compiles out with --disable-wolfcrypt.

NB, test uses a bound policy sesh. WIth an unbound, unsalted sesh, wolfTPM2_PolicyAuthValue computes the wrong HMAC and the TPM returns TPM_RC_AUTH_FAIL. Bug on master, so I can open a separate issue for it.

Cover the policyAuth branch: sign with a PolicyAuthValue-only ML-DSA
key whose policy session sits in slot 1, then assert the session
handle stays, its nonceTPM advances, and a second policy + sign on the
same session succeeds. The session is bound so the wrapper's
PolicyAuthValue HMAC key has a session key in front of the authValue.
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.

5 participants