Skip to content

feat(xtest): add ML-KEM rewrap session-key roundtrip test - #571

Open
dmihalcik-virtru wants to merge 2 commits into
mainfrom
DSPX-4221-pq-sessions
Open

feat(xtest): add ML-KEM rewrap session-key roundtrip test#571
dmihalcik-virtru wants to merge 2 commits into
mainfrom
DSPX-4221-pq-sessions

Conversation

@dmihalcik-virtru

@dmihalcik-virtru dmihalcik-virtru commented Jul 31, 2026

Copy link
Copy Markdown
Member

Summary

Implements the test-first step of DSPX-4221 (Session Keys should support ML-KEM): a client-generated ML-KEM ephemeral key pair used as the rewrap session key (the key KAS wraps the response DEK back to), as distinct from the existing mechanism-mlkem/mechanism-xwing/mechanism-secpmlkem coverage which only exercises KAS-managed TDF wrapping keys.

Changes

  • xtest/tdfs.py: new session-key-mlkem feature flag (detected alongside mechanism-mlkem, since both are gated server-side by the same Preview.MLKEMTDFEnabled flag). SDK.decrypt() gains a session_key_algorithm param, threaded through a new XT_WITH_SESSION_KEY_ALGORITHM env var.
  • xtest/sdk/{go,java,js}/cli.sh: wire the new env var to each CLI's real flag (--session-key-algorithm / --rewrap-key-type / --rewrapKeyType), and add supports session-key-mlkem probes. The Java probe joins wrapped help output before grepping — picocli line-wraps the long --rewrap-key-type choice list at ~80 columns, which can split mlkem:768 across two lines and produce a false negative (caught by running against a real build, not just eyeballing --help output).
  • xtest/test_pqc.py: new test_session_key_mlkem_roundtrip, parametrized over mlkem:768/mlkem:1024. Encrypts with a plain RSA-wrapped attribute (attribute_default_rsa) so a failure can only be attributed to the session-key transport channel, not to KAS-managed PQC mechanism support.

Audit-log assertion (not just a successful decrypt)

A successful decrypt alone doesn't prove KAS actually used ML-KEM for the session key — if an SDK silently ignored the requested algorithm and fell back to RSA, the roundtrip would still pass either way, since both sides would agree on whatever was actually used. To make the test load-bearing, it now asserts against KAS's own rewrap audit log, which is independent of anything the client reports about itself:

  • xtest/audit_logs.py: ParsedAuditEvent gains a session_key_type property (eventMetaData.sessionKeyType); matches_rewrap/assert_rewrap/assert_rewrap_success/assert_rewrap_failure gain a matching session_key_type parameter, mirroring the existing algorithm parameter.
  • The test now does audit_logs.assert_rewrap_success(session_key_type=session_key_algorithm, since_mark=mark) after decrypting.

This depends on the companion platform PR (below) adding the sessionKeyType field to the rewrap audit event — without it, session_key_type is simply absent from eventMetaData and the assertion fails.

Skips cleanly (with a clear reason) on platforms/SDKs that don't yet support ML-KEM session keys.

Verified end-to-end locally

Brought up a live platform + KAS instances (built from opentdf/platform#3814) and ran this test against Go, Java, and JS SDKs built from their respective companion-PR branches:

18 passed (go×go, go×java, go×js, java×go, java×java, java×js, js×go, js×java, js×js — each ×768/×1024)

All 18 include a real session_key_type audit-log match, not just a successful roundtrip. This run is also what caught a real web-sdk bug (opentdf/web-sdk#975) where the requested session-key algorithm was silently dropped and every decrypt negotiated RSA regardless of what was requested — the audit assertion is what surfaced it; a plain roundtrip-success check would have missed it entirely.

Related work

Companion PRs:

Test plan

cd xtest
uv run ruff check .
uv run ruff format --check .
uv run pyright audit_logs.py tdfs.py test_pqc.py

All pass. Plus the live 18/18 run described above.

Ref: DSPX-4221

Summary by CodeRabbit

  • New Features

    • Added ML-KEM session-key support for decryption across Go, Java, JavaScript, and Python SDK workflows.
    • Added configurable ML-KEM-768 and ML-KEM-1024 session-key algorithms.
    • Added capability detection for supported ML-KEM session-key environments.
  • Bug Fixes

    • Improved decryption configuration handling to apply the selected session-key algorithm correctly.
  • Tests

    • Added end-to-end validation for encryption, decryption, data integrity, and audit records.
    • Enhanced audit verification to identify the session-key type used during rewrapping.

@dmihalcik-virtru
dmihalcik-virtru requested review from a team as code owners July 31, 2026 21:35
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@dmihalcik-virtru, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f715cb6a-8e8f-4da7-8a88-b2ccf6943dc1

📥 Commits

Reviewing files that changed from the base of the PR and between 0cefcf0 and 17eda9c.

📒 Files selected for processing (8)
  • spec/DSPX-4221.md
  • xtest/audit_logs.py
  • xtest/sdk/go/cli.sh
  • xtest/sdk/java/cli.sh
  • xtest/sdk/js/cli.sh
  • xtest/tdfs.py
  • xtest/test_pqc.py
  • xtest/test_tdfs.py
📝 Walkthrough

Walkthrough

The change defines ML-KEM session-key support across the specification, shared SDK test framework, Go, Java, and JavaScript CLI wrappers. It adds audit-log matching and round-trip tests for ML-KEM-768 and ML-KEM-1024.

Changes

ML-KEM session-key support

Layer / File(s) Summary
Session-key contract and SDK API
spec/DSPX-4221.md, xtest/tdfs.py
The specification defines ML-KEM session-key behavior and acceptance criteria. The shared SDK API adds configuration, feature detection, and environment export.
Cross-SDK CLI integration
xtest/sdk/go/cli.sh, xtest/sdk/java/cli.sh, xtest/sdk/js/cli.sh
The wrappers document the configuration, detect session-key-mlkem, and pass the selected algorithm to decryption.
Session-key audit matching
xtest/audit_logs.py
Audit-log helpers read eventMetaData.sessionKeyType and use it to match rewrap events and report assertion criteria.
ML-KEM round-trip validation
xtest/test_pqc.py
Parameterized tests cover ML-KEM-768 and ML-KEM-1024 decryption, KAO validation, plaintext integrity, and rewrap audit logs.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Test as xtest/test_pqc.py
  participant SDK as SDK.decrypt
  participant Wrapper as SDK CLI wrapper
  participant CLI as otdfctl
  participant Audit as AuditLogAsserter
  Test->>SDK: Set ML-KEM session-key algorithm
  SDK->>Wrapper: Export XT_WITH_SESSION_KEY_ALGORITHM
  Wrapper->>CLI: Pass rewrap-key option
  CLI-->>SDK: Return decrypted plaintext
  SDK-->>Test: Return plaintext
  Test->>Audit: Match session_key_type in rewrap event
  Audit-->>Test: Confirm requested session-key type
Loading

Possibly related PRs

Suggested reviewers: pflynn-virtru, sujankota

Poem

A rabbit selects the key,
ML-KEM flows through carefully.
The CLI carries the type,
Audit logs confirm it right.
Plaintext returns free.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding xtest coverage for ML-KEM rewrap session-key round trips.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch DSPX-4221-pq-sessions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
xtest/tdfs.py (1)

578-596: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Define precedence for ecwrap vs session_key_algorithm.

SDK.decrypt() can pass both options, but the Go, Java, and JavaScript CLI shims emit them in separate order. This lets the CLI see two rewrap/session-key values and either fail or pick one implicitly. Reject the combination or emit one option with explicit precedence, such as session_key_algorithm overriding ecwrap.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xtest/tdfs.py` around lines 578 - 596, Update the option construction in the
decrypt helper around session_key_algorithm and ecwrap to define deterministic
precedence when both are provided. Reject the combination explicitly or emit
only one setting, with session_key_algorithm taking precedence over ecwrap, and
ensure the generated environment cannot contain conflicting rewrap/session-key
values.
🧹 Nitpick comments (2)
spec/DSPX-4221.md (1)

19-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use client-generated in the session-key description.

Replace client generated key pair with client-generated key pair.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/DSPX-4221.md` at line 19, Update the session encryption key description
in DSPX-4221 to use the hyphenated phrase “client-generated key pair” instead of
“client generated key pair,” without changing the surrounding requirement.

Source: Linters/SAST tools

xtest/sdk/go/cli.sh (1)

209-211: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use [[ in both new Bash conditionals.

SonarCloud reports the same conditional-style issue at both sites.

  • xtest/sdk/go/cli.sh#L209-L211: replace [ -n "$XT_WITH_SESSION_KEY_ALGORITHM" ] with [[ -n "$XT_WITH_SESSION_KEY_ALGORITHM" ]].
  • xtest/sdk/java/cli.sh#L198-L200: replace [ -n "$XT_WITH_SESSION_KEY_ALGORITHM" ] with [[ -n "$XT_WITH_SESSION_KEY_ALGORITHM" ]].
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xtest/sdk/go/cli.sh` around lines 209 - 211, Update the
XT_WITH_SESSION_KEY_ALGORITHM condition in xtest/sdk/go/cli.sh at lines 209-211
and xtest/sdk/java/cli.sh at lines 198-200 to use Bash [[ -n ... ]] syntax
instead of single brackets, preserving the existing argument-appending behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@xtest/tdfs.py`:
- Around line 152-156: Make ML-KEM session-key capability detection
algorithm-specific across xtest/tdfs.py:152-156 and 252-254,
xtest/sdk/go/cli.sh:119-123, xtest/sdk/java/cli.sh:129-133,
xtest/sdk/js/cli.sh:104-108, and xtest/test_pqc.py:379-403: define or expose
distinct capabilities for mlkem:768 and mlkem:1024, include mlkem:1024 in
fallback handling while preserving exact algorithm results, have each SDK probe
and report only the requested algorithm, and gate each parameterized PQC test
with its matching capability.

In `@xtest/test_pqc.py`:
- Around line 399-400: Update the pytest.skip call in the focus-filter logic to
provide an actionable reason, including the excluded encrypt_sdk and decrypt_sdk
values or clearly describing the focus rule; preserve the existing skip
condition.
- Line 421: Update the filecmp.cmp assertion in the decrypted-output test to
pass shallow=False, ensuring pt_file and rt_file are compared by byte content
rather than metadata alone.

---

Outside diff comments:
In `@xtest/tdfs.py`:
- Around line 578-596: Update the option construction in the decrypt helper
around session_key_algorithm and ecwrap to define deterministic precedence when
both are provided. Reject the combination explicitly or emit only one setting,
with session_key_algorithm taking precedence over ecwrap, and ensure the
generated environment cannot contain conflicting rewrap/session-key values.

---

Nitpick comments:
In `@spec/DSPX-4221.md`:
- Line 19: Update the session encryption key description in DSPX-4221 to use the
hyphenated phrase “client-generated key pair” instead of “client generated key
pair,” without changing the surrounding requirement.

In `@xtest/sdk/go/cli.sh`:
- Around line 209-211: Update the XT_WITH_SESSION_KEY_ALGORITHM condition in
xtest/sdk/go/cli.sh at lines 209-211 and xtest/sdk/java/cli.sh at lines 198-200
to use Bash [[ -n ... ]] syntax instead of single brackets, preserving the
existing argument-appending behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 05ddc315-d152-4758-ad81-2a05754b1f38

📥 Commits

Reviewing files that changed from the base of the PR and between 268b2f8 and baacb54.

📒 Files selected for processing (6)
  • spec/DSPX-4221.md
  • xtest/sdk/go/cli.sh
  • xtest/sdk/java/cli.sh
  • xtest/sdk/js/cli.sh
  • xtest/tdfs.py
  • xtest/test_pqc.py

Comment thread xtest/tdfs.py
Comment thread xtest/test_pqc.py Outdated
Comment thread xtest/test_pqc.py Outdated

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@xtest/sdk/go/cli.sh`:
- Line 209: Update the new XT_WITH_SESSION_KEY_ALGORITHM conditionals to use
Bash [[ -n ... ]] syntax in xtest/sdk/go/cli.sh lines 209-209 and
xtest/sdk/java/cli.sh lines 200-200, preserving the existing non-empty check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 85a7130d-9c13-48e1-b7df-a64fb5ca9b08

📥 Commits

Reviewing files that changed from the base of the PR and between baacb54 and 56a9e8f.

📒 Files selected for processing (6)
  • xtest/audit_logs.py
  • xtest/sdk/go/cli.sh
  • xtest/sdk/java/cli.sh
  • xtest/sdk/js/cli.sh
  • xtest/tdfs.py
  • xtest/test_pqc.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • xtest/sdk/js/cli.sh
  • xtest/tdfs.py
  • xtest/test_pqc.py

Comment thread xtest/sdk/go/cli.sh Outdated
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-4221-pq-sessions branch 2 times, most recently from 9ffd4d6 to 1f7c8b2 Compare August 1, 2026 13:22
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

X-Test Failure Report

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@spec/DSPX-4221.md`:
- Line 53: Update the session-key algorithm statement in DSPX-4221 to match the
existing test coverage by removing “and vice versa” from the independence claim;
do not add a reverse-combination test.
- Around line 34-36: Update test_session_key_mlkem_roundtrip readiness gating in
tdfs.py to require a session-key-specific ML-KEM capability probe or an explicit
platform build that accepts ML-KEM clientPublicKey, rather than relying solely
on the shared session-key-mlkem flag. Preserve the test’s existing audit-log
sessionKeyType assertion once the capability check passes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ee99aa8a-c8d3-462e-85ec-725886e64223

📥 Commits

Reviewing files that changed from the base of the PR and between 9ffd4d6 and 0cefcf0.

📒 Files selected for processing (7)
  • spec/DSPX-4221.md
  • xtest/audit_logs.py
  • xtest/sdk/go/cli.sh
  • xtest/sdk/java/cli.sh
  • xtest/sdk/js/cli.sh
  • xtest/tdfs.py
  • xtest/test_pqc.py
🚧 Files skipped from review as they are similar to previous changes (6)
  • xtest/sdk/js/cli.sh
  • xtest/sdk/go/cli.sh
  • xtest/test_pqc.py
  • xtest/sdk/java/cli.sh
  • xtest/audit_logs.py
  • xtest/tdfs.py

Comment thread spec/DSPX-4221.md Outdated
Comment thread spec/DSPX-4221.md Outdated
Address CodeRabbit review comments on spec/DSPX-4221.md:
- Note the platform-side session-key-mlkem feature-detection imprecision
  (reuses the pre-existing Preview.MLKEMTDFEnabled/mechanism probe rather
  than a dedicated readiness check; each SDK's own hardcoded capability
  flag is what actually gates the test), matching the comment already in
  tdfs.py's feature-detection block.
- Fix invalid Go syntax in the SDK contract example (ocrypto.KeyType is a
  string type; bitwise-OR between two enum values doesn't compile).
- Remove the "and vice versa" independence claim, which overstated test
  coverage (only RSA-wrapped-TDF + ML-KEM-session-key is tested).

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@sonarqubecloud

sonarqubecloud Bot commented Aug 1, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds end-to-end xtest coverage for ML-KEM as the client-generated rewrap session key, including verification via KAS rewrap audit logs so tests assert the negotiated session-key type (not just successful decrypt).

Changes:

  • Add session_key_algorithm plumbing to tdfs.SDK.decrypt() via XT_WITH_SESSION_KEY_ALGORITHM, and wire it through Go/Java/JS CLI wrappers (plus SDK capability probes).
  • Extend audit-log parsing/assertion utilities to match on eventMetaData.sessionKeyType.
  • Add new PQC tests covering ML-KEM-768/1024 session-key roundtrips and audit-log verification; update existing EC rewrap audit assertion to pin the negotiated session-key type.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
xtest/test_tdfs.py Tightens EC rewrap audit assertion to check session_key_type.
xtest/test_pqc.py Adds ML-KEM session-key roundtrip test (768/1024) plus RSA session-key audit-field sanity test.
xtest/tdfs.py Introduces session-key-mlkem feature flag and threads session_key_algorithm into decrypt via env var.
xtest/sdk/go/cli.sh Wires XT_WITH_SESSION_KEY_ALGORITHM to --session-key-algorithm and adds a supports session-key-mlkem probe.
xtest/sdk/java/cli.sh Wires XT_WITH_SESSION_KEY_ALGORITHM to --rewrap-key-type and adds a supports session-key-mlkem probe.
xtest/sdk/js/cli.sh Wires XT_WITH_SESSION_KEY_ALGORITHM to --rewrapKeyType and adds a supports session-key-mlkem probe.
xtest/audit_logs.py Adds ParsedAuditEvent.session_key_type and session_key_type filtering to rewrap assertions.
spec/DSPX-4221.md Documents DSPX-4221 scope/acceptance criteria and links companion PRs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread xtest/tdfs.py
Comment on lines 601 to +606
if assert_keys:
local_env |= {"XT_WITH_ASSERTION_VERIFICATION_KEYS": assert_keys}
if ecwrap:
local_env |= {"XT_WITH_ECWRAP": "true"}
if session_key_algorithm:
local_env |= {"XT_WITH_SESSION_KEY_ALGORITHM": session_key_algorithm}
Comment thread xtest/test_tdfs.py
Comment on lines +111 to +113
audit_logs.assert_rewrap_success(
session_key_type="ec:secp256r1", min_count=1, since_mark=ec_mark
)
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