Skip to content

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

Draft
dmihalcik-virtru wants to merge 2 commits into
mainfrom
DSPX-2399-web-sdk
Draft

feat(web-sdk): add ML-KEM-768 post-quantum key encapsulation (DSPX-2399)#934
dmihalcik-virtru wants to merge 2 commits into
mainfrom
DSPX-2399-web-sdk

Conversation

@dmihalcik-virtru
Copy link
Copy Markdown
Member

Summary

Implements ML-KEM-768 post-quantum key encapsulation mechanism for the web SDK as part of the mechanism-mlkem feature (DSPX-2399).

  • Adds 'mlkem:768' to KasPublicKeyAlgorithm union type and related validator/converter functions
  • Adds MLKEM_768 = 'ML-KEM-768' enum entry in lib/src/crypto/enums.ts
  • Extends parsePublicKeyPem and type declarations to detect and handle ML-KEM-768 SPKI keys (OID 2.16.840.1.101.3.4.4.2)
  • Adds @noble/post-quantum dependency for pure-JS ML-KEM-768 encapsulation
  • Introduces MLKEMWrapped class with wire format: KAO type="wrapped", wrappedKey=base64(ml_kem_ciphertext[1088] || aes_kw_wrapped_dek), no ephemeralPublicKey
  • Routes 'mlkem:768' algorithm through buildKeyAccessMLKEMWrapped
  • Adds xtest/sdk/js/cli.sh with supports mechanism-mlkem case for the cross-platform test harness

References

Test plan

  • TypeScript builds cleanly (tsc --noEmit)
  • ESLint passes (npm run lint)
  • Existing unit tests unaffected (pre-existing server-dependent failures unchanged)
  • xtest/sdk/js/cli.sh supports mechanism-mlkem exits 0
  • Integration roundtrip: xtest/scenarios/mechanism-mlkem.yaml (requires platform-service cell)

🤖 Generated with Claude Code

- Add 'mlkem:768' to KasPublicKeyAlgorithm union type in lib/src/access.ts
- Add MLKEM_768 enum entry to AlgorithmName in lib/src/crypto/enums.ts
- Add 'mlkem:768' to KeyAlgorithm and PublicKeyInfo.algorithm in declarations.ts
- Detect ML-KEM-768 OID in parsePublicKeyPem (key-format.ts)
- Implement encapsulation helpers in lib/src/crypto/mlkem.ts using @noble/post-quantum
- Add MLKEMWrapped class: KAO type=wrapped, wrappedKey=base64(ct[1088]||aes-kw-dek)
- Route 'mlkem:768' to MLKEMWrapped in buildKeyAccess and client/index.ts
- Add xtest/sdk/js/cli.sh with supports mechanism-mlkem

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 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: aea03f6f-4f9e-4cb0-bc28-7d8144efaba0

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-web-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

If these changes look good, signoff on them with:

git pull && git commit --amend --signoff && git push --force-with-lease origin

If they aren't any good, please remove them with:

git pull && git reset --hard HEAD~1 && git push --force-with-lease origin

Copy link
Copy Markdown

@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 post-quantum key encapsulation mechanism. Key changes include adding the @noble/post-quantum dependency, implementing ML-KEM-768 utilities for SPKI parsing and encapsulation, and integrating the new algorithm into the TDF3 client and key access models. Review feedback identifies an opportunity to improve OID validation by checking specific offsets rather than using a substring search and recommends centralizing shared constants and logic to avoid duplication across the codebase.

Comment thread lib/src/crypto/mlkem.ts
Comment on lines +18 to +20
if (!hex.includes(MLKEM768_OID_HEX)) {
throw new Error('Not an ML-KEM-768 SPKI public key');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using hex.includes(MLKEM768_OID_HEX) to validate the SPKI structure is potentially unreliable, as the OID byte sequence could theoretically appear within the pseudorandom key material. Since the SPKI header for ML-KEM-768 has a fixed length, it is safer to verify the OID at its expected position (offset 12 in the hex string for a standard DER-encoded SPKI).

Suggested change
if (!hex.includes(MLKEM768_OID_HEX)) {
throw new Error('Not an ML-KEM-768 SPKI public key');
}
if (hex.substring(12, 12 + MLKEM768_OID_HEX.length) !== MLKEM768_OID_HEX) {
throw new Error('Not an ML-KEM-768 SPKI public key');
}

Comment on lines +98 to +118
// ML-KEM-768 OID: 2.16.840.1.101.3.4.4.2 encoded in DER as tag (06) + length (09) + value
const MLKEM768_OID_HEX = '0609608648016503040402';
const MLKEM768_EK_BYTES = 1184;
// Bytes before the raw key in a well-formed ML-KEM-768 SPKI blob:
// 4 outer SEQUENCE header + 13 AlgorithmIdentifier + 4 BIT STRING header + 1 unused-bits = 22
const MLKEM768_SPKI_EK_OFFSET = 22;

/**
* Extract the 1184-byte ML-KEM-768 encapsulation key from a SPKI-encoded public key.
*/
export function extractMLKEM768EkFromSpki(spkiBytes: ArrayBuffer): Uint8Array {
const hex = hexEncode(spkiBytes);
if (!hex.includes(MLKEM768_OID_HEX)) {
throw new ConfigurationError('Not an ML-KEM-768 SPKI public key');
}
const view = new Uint8Array(spkiBytes);
if (view.length !== MLKEM768_SPKI_EK_OFFSET + MLKEM768_EK_BYTES) {
throw new ConfigurationError(`Invalid ML-KEM-768 SPKI length: ${view.length}`);
}
return view.slice(MLKEM768_SPKI_EK_OFFSET);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This block duplicates constants and logic already defined in lib/src/crypto/mlkem.ts. To maintain a single source of truth and improve maintainability, these should be imported from the core crypto module (e.g., import { MLKEM768_OID_HEX, MLKEM768_EK_BYTES, extractMLKEM768EkFromSpki } from '../../../../src/crypto/mlkem.js'). If you need to preserve the ConfigurationError type specifically for this module, you can wrap the imported function call.

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