Skip to content

feat(sdk): support ML-KEM rewrap session keys - #388

Open
dmihalcik-virtru wants to merge 1 commit into
mainfrom
DSPX-4221-pq-sessions
Open

feat(sdk): support ML-KEM rewrap session keys#388
dmihalcik-virtru wants to merge 1 commit into
mainfrom
DSPX-4221-pq-sessions

Conversation

@dmihalcik-virtru

@dmihalcik-virtrudmihalcik-virtru commented Jul 31, 2026

Copy link
Copy Markdown
Member

Summary

Part of DSPX-4221 (Session Keys should support ML-KEM). KASClient.unwrap() only branched EC vs RSA for the client's ephemeral rewrap "session key" (as opposed to a KAS-managed wrapping key), so requesting an ML-KEM session key would fail. KeyType already had MLKEM768Key/MLKEM1024Key entries and sdk-pqc-bc already had the BouncyCastle KEM primitives (used for the KAS-managed mechanism-key path), but nothing generated a fresh ML-KEM keypair for the session-key role.

Changes

  • spi/KemProvider: add generateKeyPair(KeyType), returning a new KeyPairPem (PEM-encoded SPKI/PKCS#8 pair). The core sdk module has no compile-time dependency on BouncyCastle (per ADR 0001), so the actual keypair generation lives in the optional sdk-pqc-bc module and is reached via the existing ServiceLoader-based KemProviders registry — same pattern already used for wrapDEK/unwrapDEK.
  • sdk-pqc-bc/HybridCrypto: add a generateKeyPair dispatch mirroring the existing wrapDEK/unwrapDEK switch, covering all five supported PQC key types (X-Wing, both NIST hybrids, ML-KEM-768/1024) so the SPI contract stays complete for every type in supportedKeyTypes().
  • sdk-pqc-bc/BouncyCastleKemProvider: implement the new SPI method.
  • KASClient.unwrap(): branch on sessionKeyType.isMLKEM(), generating a keypair via KemProviders.get(...).generateKeyPair(...) and decrypting the response via KemProviders.get(...).unwrapDEK(...).
  • New unit test HybridCryptoGenerateKeyPairTest round-trips generateKeyPair -> wrapDEK -> unwrapDEK for all five PQC key types.

cmdline's --rewrap-key-type already accepts mlkem:768/mlkem:1024 with no code changes needed: picocli's enum candidate list is derived from KeyType#toString(), which already returns "mlkem:768"/"mlkem:1024". Verified by building cmdline.jar and checking help decrypt output.

Test plan

mvn -pl sdk,sdk-pqc-bc -am test

All existing tests plus the new HybridCryptoGenerateKeyPairTest (5/5) pass. Also built cmdline.jar and confirmed --rewrap-key-type=mlkem:768 parses correctly (fails downstream only on the expected network/gRPC call, not on argument validation).

Related work

Companion PRs:

Ref: DSPX-4221

Summary by CodeRabbit

  • New Features

    • Added support for ML-KEM session-key unwrapping.
    • Added key-pair generation for supported hybrid and ML-KEM algorithms.
    • Made generated public and private keys available in PEM format.
    • Added ephemeral key-pair support for cryptographic workflows.
    • Updated the supports command to report ML-KEM session-key support.
  • Bug Fixes

    • Improved ML-KEM key validation and end-to-end wrap/unwrap handling.
    • Preserved existing EC and RSA key-handling behavior.

@dmihalcik-virtru
dmihalcik-virtru requested review from a team as code ownersJuly 31, 2026 21:36
@coderabbitai

coderabbitaiBot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

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:39 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: 2a7852fe-22a6-4345-8894-2b3406487dc5

📥 Commits

Reviewing files that changed from the base of the PR and between f15299e and 52b7d4f.

📒 Files selected for processing (7)
  • cmdline/src/main/java/io/opentdf/platform/Command.java
  • cmdline/src/test/java/io/opentdf/platform/CommandTest.java
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/BouncyCastleKemProvider.java
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/HybridCrypto.java
  • sdk-pqc-bc/src/test/java/io/opentdf/platform/sdk/pqc/bc/HybridCryptoGenerateKeyPairTest.java
  • sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
  • sdk/src/main/java/io/opentdf/platform/sdk/spi/KemProvider.java
📝 Walkthrough

Walkthrough

The PR adds PEM key-pair generation to the KEM provider API and Bouncy Castle implementation. KASClient.unwrap uses generated ML-KEM keys during rewrap handling. Tests validate supported key types and DEK round trips. The supports command reports ML-KEM session-key support.

Changes

KEM key generation and ML-KEM unwrap

Layer / File(s)Summary
KEM key-generation contract and implementation
sdk/src/main/java/io/opentdf/platform/sdk/spi/KemProvider.java, sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/...
KemProvider exposes PEM key-pair generation. HybridCrypto dispatches supported key types, and BouncyCastleKemProvider delegates to it.
ML-KEM unwrap integration
sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
KASClient.unwrap generates session material, sends the public key, and unwraps the response through the matching provider.
Key-generation round-trip validation
sdk-pqc-bc/src/test/java/io/opentdf/platform/sdk/pqc/bc/HybridCryptoGenerateKeyPairTest.java
Parameterized tests cover five key types, PEM headers, and DEK wrap/unwrap equality.
ML-KEM feature reporting
cmdline/src/main/java/io/opentdf/platform/Command.java, cmdline/src/test/java/io/opentdf/platform/CommandTest.java
The supports command reports session-key-mlkem in plain-text and JSON output.

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

Sequence Diagram(s)

sequenceDiagram
participant KASClient
participant KemProviders
participant BouncyCastleKemProvider
participant KAS
KASClient->>KemProviders: resolve ML-KEM provider
KASClient->>BouncyCastleKemProvider: generateKeyPair
BouncyCastleKemProvider-->>KASClient: return PEM key pair
KASClient->>KAS: send public key in rewrap request
KAS-->>KASClient: return wrapped response key
KASClient->>BouncyCastleKemProvider: unwrap with private key
Loading

Possibly related PRs

Suggested reviewers:mkleene

Poem

A rabbit makes a PEM key pair,
Public here and private there.
ML-KEM joins the unwrap stream,
Five tests confirm the round-trip dream.
The DEK returns intact.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 28.57% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding ML-KEM support for rewrap session keys in the SDK.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 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.

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

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 `@sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java`:
- Around line 126-133: Update the unwrap flow around the ML-KEM branch to keep
the generated public key in a local request-scoped variable instead of shared
clientPublicKey. Use that local variable when assigning body.clientPublicKey,
while preserving the existing EC behavior and each call’s private-key pairing.
🪄 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: 9be24f35-9e29-4f8c-b225-82b889ac8cc4

📥 Commits

Reviewing files that changed from the base of the PR and between 57d070b and 08e8b8d.

📒 Files selected for processing (5)
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/BouncyCastleKemProvider.java
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/HybridCrypto.java
  • sdk-pqc-bc/src/test/java/io/opentdf/platform/sdk/pqc/bc/HybridCryptoGenerateKeyPairTest.java
  • sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
  • sdk/src/main/java/io/opentdf/platform/sdk/spi/KemProvider.java

Comment threadsdk/src/main/java/io/opentdf/platform/sdk/KASClient.java Outdated
@github-actions

Copy link
Copy Markdown
Contributor

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

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 `@sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java`:
- Around line 136-153: Make the RSA lazy initialization in
generateSessionKeyMaterial thread-safe using double-checked locking: retain the
fast null check, synchronize the initialization block on the appropriate shared
lock, and recheck decryptor before generating keys. Mark both decryptor and
clientPublicKey volatile so the initialized key pair is safely published and the
public/private keys remain consistent across concurrent unwrap calls.
🪄 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: 40b7a1b3-6d7c-4bbb-bcd3-ddb7462ba145

📥 Commits

Reviewing files that changed from the base of the PR and between 08e8b8d and c8ece6d.

📒 Files selected for processing (5)
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/BouncyCastleKemProvider.java
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/HybridCrypto.java
  • sdk-pqc-bc/src/test/java/io/opentdf/platform/sdk/pqc/bc/HybridCryptoGenerateKeyPairTest.java
  • sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
  • sdk/src/main/java/io/opentdf/platform/sdk/spi/KemProvider.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • sdk/src/main/java/io/opentdf/platform/sdk/spi/KemProvider.java
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/HybridCrypto.java
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/BouncyCastleKemProvider.java
  • sdk-pqc-bc/src/test/java/io/opentdf/platform/sdk/pqc/bc/HybridCryptoGenerateKeyPairTest.java

Comment threadsdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
@github-actions

Copy link
Copy Markdown
Contributor

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

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 `@cmdline/src/main/java/io/opentdf/platform/Command.java`:
- Line 80: Update the FEATURES construction in Command so session-key-mlkem is
included only when the KemProvider registry reports an ML-KEM provider is
registered, using the existing KemProviders.registered() mechanism. Keep dpop
and dpop_nonce_challenge unconditional, and avoid relying on sdk-pqc-bc build
profiles or classpath presence.
🪄 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: 72e05d1a-3e79-44ca-b641-7bd957ff59b1

📥 Commits

Reviewing files that changed from the base of the PR and between c8ece6d and f15299e.

📒 Files selected for processing (7)
  • cmdline/src/main/java/io/opentdf/platform/Command.java
  • cmdline/src/test/java/io/opentdf/platform/CommandTest.java
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/BouncyCastleKemProvider.java
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/HybridCrypto.java
  • sdk-pqc-bc/src/test/java/io/opentdf/platform/sdk/pqc/bc/HybridCryptoGenerateKeyPairTest.java
  • sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
  • sdk/src/main/java/io/opentdf/platform/sdk/spi/KemProvider.java
🚧 Files skipped from review as they are similar to previous changes (5)
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/BouncyCastleKemProvider.java
  • sdk-pqc-bc/src/test/java/io/opentdf/platform/sdk/pqc/bc/HybridCryptoGenerateKeyPairTest.java
  • sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/HybridCrypto.java
  • sdk/src/main/java/io/opentdf/platform/sdk/spi/KemProvider.java
  • sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java

Comment threadcmdline/src/main/java/io/opentdf/platform/Command.java Outdated
@sonarqubecloud

Copy link
Copy Markdown

Part of DSPX-4221 (Session Keys should support ML-KEM). KASClient.unwrap()
only branched EC vs RSA for the client's ephemeral rewrap "session key",
so requesting an ML-KEM session key would fail. KeyType already had
MLKEM768Key/MLKEM1024Key entries and sdk-pqc-bc already had the
BouncyCastle KEM primitives (used for KAS-managed mechanism keys), but
nothing generated a fresh ML-KEM keypair for the session-key role.
- spi/KemProvider: add generateKeyPair(KeyType), returning a new
KeyPairPem (PEM-encoded SPKI/PKCS#8 pair). The core sdk module has no
compile-time dependency on BouncyCastle, so the actual keypair
generation lives in the optional sdk-pqc-bc module and is reached via
the existing ServiceLoader-based KemProviders registry.
- sdk-pqc-bc/HybridCrypto: add a generateKeyPair dispatch mirroring the
existing wrapDEK/unwrapDEK switch, covering all five supported PQC key
types (X-Wing, both NIST hybrids, ML-KEM-768/1024).
- sdk-pqc-bc/BouncyCastleKemProvider: implement the new SPI method.
- KASClient.unwrap(): branch on sessionKeyType.isMLKEM(), generating a
keypair via KemProviders.get(...).generateKeyPair(...) and decrypting
the response with KemProviders.get(...).unwrapDEK(...).
- New unit test (HybridCryptoGenerateKeyPairTest) round-trips
generateKeyPair -> wrapDEK -> unwrapDEK for all five PQC key types.
- cmdline: add "session-key-mlkem" to the `supports` subcommand's
feature list, gated on KemProviders.registered() actually containing an
ML-KEM provider rather than being unconditionally true (so a FIPS-profile
build, which excludes sdk-pqc-bc, correctly reports it as unsupported
instead of claiming a capability that would throw at runtime).
Addresses CodeRabbit review feedback:
- unwrap() previously wrote the generated session public key to the shared
clientPublicKey instance field before serializing the request. A
concurrent unwrap() call on the same KASClient could overwrite that field
first, causing this call to send the wrong public key while decrypting
with its own private key. Refactored so each call's session-key material
(public PEM plus whichever private key it needs for the response) is a
call-scoped SessionKeyMaterial value, threaded explicitly through
generateSessionKeyMaterial()/unwrapResponseKey() rather than shared
mutable fields. This split also resolves a SonarCloud cognitive-complexity
(java:S3776) failure on unwrap() from combining request-build and
response-decrypt branching in one method.
- Follow-up on the same class of bug: the RSA branch's lazy
keypair-initialization was still a check-then-act race on non-volatile
fields (decryptor/clientPublicKey), so two concurrent first callers could
generate distinct keypairs and race to overwrite each other's fields.
Fixed with double-checked locking and volatile fields.
- Command.Supports' FEATURES list unconditionally claimed "session-key-mlkem"
regardless of whether an ML-KEM KemProvider is actually registered (e.g.
under the fips Maven profile, which excludes sdk-pqc-bc). Now computed
dynamically via KemProviders.registered().
Ref: DSPX-4221
Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

CopilotAI 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 ML-KEM (FIPS 203) support for the client-generated “session key” path used in KAS rewrap/unwrap, by extending the KEM SPI to allow ephemeral keypair generation and wiring that into KASClient.unwrap(). This keeps the core sdk module provider-agnostic (no BC dependency) while enabling PQC behavior via the existing ServiceLoader registry.

Changes:

  • Extend KemProvider SPI with generateKeyPair(KeyType) and implement it in sdk-pqc-bc.
  • Update KASClient.unwrap() to generate ML-KEM session keypairs via KemProviders and unwrap responses via unwrapDEK.
  • Add PQC unit test coverage for generateKeyPair -> wrapDEK -> unwrapDEK round-trips, and update cmdline supports feature reporting.

Reviewed changes

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

Show a summary per file
FileDescription
sdk/src/main/java/io/opentdf/platform/sdk/spi/KemProvider.javaAdds SPI method for ephemeral KEM keypair generation and a PEM keypair carrier type.
sdk/src/main/java/io/opentdf/platform/sdk/KASClient.javaAdds ML-KEM session-key branch and refactors unwrap flow to support EC/RSA/ML-KEM.
sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/HybridCrypto.javaImplements keypair generation dispatch for all supported PQC key types.
sdk-pqc-bc/src/main/java/io/opentdf/platform/sdk/pqc/bc/BouncyCastleKemProvider.javaImplements the new SPI method by delegating to HybridCrypto.
sdk-pqc-bc/src/test/java/io/opentdf/platform/sdk/pqc/bc/HybridCryptoGenerateKeyPairTest.javaAdds parameterized test ensuring generated keypairs can wrap/unwrap a DEK for all PQC types.
cmdline/src/main/java/io/opentdf/platform/Command.javaUpdates supports to advertise session-key-mlkem when an ML-KEM provider is present.
cmdline/src/test/java/io/opentdf/platform/CommandTest.javaUpdates supports-output expectations to include session-key-mlkem.

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

* @param keyType hybrid or pure-PQC algorithm; must be a member of {@link #supportedKeyTypes()}
* @return the generated keypair, PEM-encoded
*/
KeyPairPem generateKeyPair(KeyType keyType);
Comment on lines +156 to +158
var encryptionKeypair = CryptoUtils.generateRSAKeypair();
decryptor = new AsymDecryption(encryptionKeypair.getPrivate());
clientPublicKey = CryptoUtils.getRSAPublicKeyPEM(encryptionKeypair.getPublic());
@Arpan0995

Copy link
Copy Markdown

I came across this PR while looking at ML-KEM adoption across Java libraries and read the rewrap path against FIPS 203 and the envelope format. The core choices look right: the ML-KEM session key is a fresh ephemeral per unwrap() call (matching the EC path's per-call ECKeyPair, which is the property you want for a rewrap session key); the envelope parsing in HybridCrypto is strict (canonical DER lengths, trailing bytes rejected, bounds-checked reads); KemProviders.get() fails with a clear SDKException when the sdk-pqc-bc module is absent rather than an NPE; and the new round-trip test covers all five key types. Using the FIPS 203 shared secret directly as the wrap key for pure ML-KEM is sound (it is a uniform 32-byte key by construction) and the ADR reference makes the intent clear.

One real issue and two smaller notes.

1. The new double-checked locking leaves a read-side race on clientPublicKey (low, fail-closed). In generateSessionKeyMaterial the initializing thread writes decryptor first and clientPublicKey second (KASClient.java:157-158), but the fast path reads decryptor at :153 and, when it is non-null, returns clientPublicKey at :162 without entering the lock. A thread that arrives between the two writes sees decryptor != null, skips the synchronized block, and returns a SessionKeyMaterial whose publicKeyPem is still null, so the rewrap request is built with body.clientPublicKey = null (:202). This is plain interleaving, not a memory-model corner case, and the window is wider than one instruction: it spans the whole Base64 PEM encoding on the right-hand side of :158. I checked it with a two-thread harness mirroring the exact field and write structure, forcing a reader into that window: it read the null public key in 19,677 of 20,000 forced races, and with the two writes swapped (so the volatile publish of decryptor covers the PEM) it read null in 0 of 20,000. The forced rate is not the production frequency, which depends on thread timing, but it isolates the write-order as the cause and confirms the swap removes it. The window only exists around first use of the RSA path and the result is a rejected request rather than anything unsafe, so this is low severity, but it is the exact class of mixed-state send the comment above the block says the locking is there to prevent. The minimal fix is to swap the two writes (writing clientPublicKey before decryptor makes the decryptor volatile read at :153 guarantee visibility of the PEM); returning the PEM from inside the synchronized block, or storing both fields in a single volatile holder object, also works and is harder to regress.

2. The SPI freezes the private key as an immutable String (design note, worth deciding now).KemProvider.KeyPairPem (KemProvider.java:63-70) carries the ephemeral ML-KEM private key as a PEM String. Strings cannot be zeroized, so the decapsulation key lives on the heap until GC with no way to wipe it after the unwrap completes. The EC path has the same property today via key objects, so this is not a regression, but since this PR is the moment KeyPairPem enters the SPI contract, it is the cheapest time to choose byte[]/char[] (with a wipe after use) over String if you ever want that option; changing it later means breaking the interface.

3. Negative tests (small).HybridCryptoGenerateKeyPairTest round-trips the happy path. Given the parser already has strict failure modes, two cheap additions would pin the security-relevant behavior: unwrap with a different key pair of the same type must fail, and an envelope with a flipped byte in the ciphertext must fail rather than return a DEK. Both are a few lines in the same parameterized style.

The strict envelope parser and the per-call ephemeral are the parts that are easy to get wrong in a rewrap flow, and both look right here.

Sign up for freeto 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.

3 participants

@dmihalcik-virtru@Arpan0995