feat(java-sdk): add ML-KEM-768 post-quantum key encapsulation (DSPX-2399)#370
feat(java-sdk): add ML-KEM-768 post-quantum key encapsulation (DSPX-2399)#370dmihalcik-virtru wants to merge 1 commit into
Conversation
…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>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
X-Test Failure Report |
There was a problem hiding this comment.
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.
| 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) { |
There was a problem hiding this comment.
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.
| 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()); |
| MLKEMGenerator kemGen = new MLKEMGenerator(new SecureRandom()); | ||
| SecretWithEncapsulation swe = kemGen.generateEncapsulated(publicKeyParams); | ||
|
|
||
| byte[] ciphertext = swe.getEncapsulation(); |
There was a problem hiding this comment.
Leverage the defined CIPHERTEXT_SIZE constant to validate that the generated encapsulation matches the expected length for ML-KEM-768.
| byte[] ciphertext = swe.getEncapsulation(); | |
| byte[] ciphertext = swe.getEncapsulation(); | |
| if (ciphertext.length != CIPHERTEXT_SIZE) { | |
| throw new SDKException("invalid ML-KEM-768 ciphertext length: " + ciphertext.length); | |
| } |
Summary
Implements pure ML-KEM-768 post-quantum key encapsulation for the Java SDK as part of the
mechanism-mlkemfeature.KeyType.java: AddsMLKEM768Key("mlkem:768")enum entry withfromAlgorithmandfromPublicKeyAlgorithmmappings for the newALGORITHM_ML_KEM_768/KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 = 13proto valuesMLKEMEncryption.java: New class using Bouncy CastleMLKEMGeneratorto encapsulate a DEK; wire format isbase64(ml_kem_ciphertext [1088 bytes] || aes_gcm_wrapped_dek)— noephemeralPublicKey, KAO type"wrapped", matching the Go SDK wire formatTDF.java: RoutesisMlkem()key types tocreateMLKEMWrappedKeyincreateKeyAccesssdk/pom.xml: Updatesplatform.branchtoDSPX-2399-platform-prototo pull in the new enum value from the protobuf stubsxtest/sdk/java/cli.sh: Addssupports mechanism-mlkemcase that greps picocli's encrypt help for"mlkem:768"to activate integration testsJira
https://virtru.atlassian.net/browse/DSPX-2399
Test scenario
xtest/scenarios/mechanism-mlkem.yaml
Test plan
DSPX-2399-platform-protostubsxtest/sdk/java/cli.sh supports mechanism-mlkemexits 0 after cmdline is builttest_mlkem768_roundtrippasses when platform service cell is ready🤖 Generated with Claude Code