Skip to content

feat(java-sdk): add ML-KEM-768 post-quantum key encapsulation (DSPX-2399)#370

Draft
dmihalcik-virtru wants to merge 1 commit into
mainfrom
DSPX-2399-java-sdk
Draft

feat(java-sdk): add ML-KEM-768 post-quantum key encapsulation (DSPX-2399)#370
dmihalcik-virtru wants to merge 1 commit into
mainfrom
DSPX-2399-java-sdk

Conversation

@dmihalcik-virtru
Copy link
Copy Markdown
Member

Summary

Implements pure ML-KEM-768 post-quantum key encapsulation for the Java SDK as part of the mechanism-mlkem feature.

  • KeyType.java: Adds MLKEM768Key("mlkem:768") enum entry with fromAlgorithm and fromPublicKeyAlgorithm mappings for the new ALGORITHM_ML_KEM_768 / KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 = 13 proto values
  • MLKEMEncryption.java: New class using Bouncy Castle MLKEMGenerator to encapsulate a DEK; wire format is base64(ml_kem_ciphertext [1088 bytes] || aes_gcm_wrapped_dek) — no ephemeralPublicKey, KAO type "wrapped", matching the Go SDK wire format
  • TDF.java: Routes isMlkem() key types to createMLKEMWrappedKey in createKeyAccess
  • sdk/pom.xml: Updates platform.branch to DSPX-2399-platform-proto to pull in the new enum value from the protobuf stubs
  • xtest/sdk/java/cli.sh: Adds supports mechanism-mlkem case that greps picocli's encrypt help for "mlkem:768" to activate integration tests

Jira

https://virtru.atlassian.net/browse/DSPX-2399

Test scenario

xtest/scenarios/mechanism-mlkem.yaml

Test plan

  • CI build compiles against DSPX-2399-platform-proto stubs
  • Unit tests pass (KeyType enum, TDF round-trip)
  • xtest/sdk/java/cli.sh supports mechanism-mlkem exits 0 after cmdline is built
  • Integration test test_mlkem768_roundtrip passes when platform service cell is ready

🤖 Generated with Claude Code

…399)

- Add MLKEM768Key("mlkem:768") enum entry to KeyType with fromAlgorithm
  and fromPublicKeyAlgorithm mappings for ALGORITHM_ML_KEM_768 /
  KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768
- Implement MLKEMEncryption using Bouncy Castle pqc; wire format is
  base64(ml_kem_ciphertext [1088 bytes] || aes_gcm_wrapped_dek) with no
  ephemeralPublicKey field; KAO type "wrapped"
- Wire MLKEMEncryption into TDF.createKeyAccess via new isMlkem() branch
- Point sdk/pom.xml platform.branch at DSPX-2399-platform-proto to pull
  in KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 = 13 proto stubs
- Add xtest/sdk/java/cli.sh supports mechanism-mlkem case

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dda5524b-d697-48fb-9ab7-cabf1fbe0392

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch DSPX-2399-java-sdk

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 and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

X-Test Failure Report

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for ML-KEM-768 key encapsulation, a post-quantum cryptographic algorithm. Key changes include the addition of the MLKEM768Key type to the KeyType enum, the implementation of the MLKEMEncryption class using Bouncy Castle, and integration within the TDF manifest creation process. Feedback focuses on improving resource management in MLKEMEncryption by using try-with-resources for the PEMParser, optimizing performance through the reuse of a static SecureRandom instance, and adding validation to ensure the generated ciphertext matches the expected length.

Comment on lines +25 to +33
private final MLKEMPublicKeyParameters publicKeyParams;

MLKEMEncryption(String pemPublicKey) {
try {
PEMParser parser = new PEMParser(new StringReader(pemPublicKey));
SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) parser.readObject();
parser.close();
publicKeyParams = (MLKEMPublicKeyParameters) PublicKeyFactory.createKey(spki);
} catch (IOException e) {
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.

medium

Improve resource management by using try-with-resources for PEMParser to ensure it is closed even if an exception occurs during key parsing. Additionally, performance can be optimized by reusing a static SecureRandom instance instead of creating a new one for every encapsulation operation.

Suggested change
private final MLKEMPublicKeyParameters publicKeyParams;
MLKEMEncryption(String pemPublicKey) {
try {
PEMParser parser = new PEMParser(new StringReader(pemPublicKey));
SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) parser.readObject();
parser.close();
publicKeyParams = (MLKEMPublicKeyParameters) PublicKeyFactory.createKey(spki);
} catch (IOException e) {
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
private final MLKEMPublicKeyParameters publicKeyParams;
MLKEMEncryption(String pemPublicKey) {
try (PEMParser parser = new PEMParser(new StringReader(pemPublicKey))) {
SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) parser.readObject();
publicKeyParams = (MLKEMPublicKeyParameters) PublicKeyFactory.createKey(spki);
} catch (IOException e) {

* @return ciphertext (1088 bytes) concatenated with the AES-GCM wrapped DEK
*/
byte[] encapsulateAndWrap(byte[] dek) {
MLKEMGenerator kemGen = new MLKEMGenerator(new SecureRandom());
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.

medium

Use the static SECURE_RANDOM instance to avoid the overhead of instantiating a new SecureRandom on every call.

Suggested change
MLKEMGenerator kemGen = new MLKEMGenerator(new SecureRandom());
MLKEMGenerator kemGen = new MLKEMGenerator(SECURE_RANDOM);

MLKEMGenerator kemGen = new MLKEMGenerator(new SecureRandom());
SecretWithEncapsulation swe = kemGen.generateEncapsulated(publicKeyParams);

byte[] ciphertext = swe.getEncapsulation();
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.

medium

Leverage the defined CIPHERTEXT_SIZE constant to validate that the generated encapsulation matches the expected length for ML-KEM-768.

Suggested change
byte[] ciphertext = swe.getEncapsulation();
byte[] ciphertext = swe.getEncapsulation();
if (ciphertext.length != CIPHERTEXT_SIZE) {
throw new SDKException("invalid ML-KEM-768 ciphertext length: " + ciphertext.length);
}

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.

1 participant