feat(web-sdk): add ML-KEM-768 post-quantum key encapsulation (DSPX-2399)#934
feat(web-sdk): add ML-KEM-768 post-quantum key encapsulation (DSPX-2399)#934dmihalcik-virtru wants to merge 2 commits into
Conversation
- 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>
|
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 |
|
If these changes look good, signoff on them with: If they aren't any good, please remove them with: |
There was a problem hiding this comment.
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.
| if (!hex.includes(MLKEM768_OID_HEX)) { | ||
| throw new Error('Not an ML-KEM-768 SPKI public key'); | ||
| } |
There was a problem hiding this comment.
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).
| 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'); | |
| } |
| // 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); | ||
| } |
There was a problem hiding this comment.
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.
Summary
Implements ML-KEM-768 post-quantum key encapsulation mechanism for the web SDK as part of the
mechanism-mlkemfeature (DSPX-2399).'mlkem:768'toKasPublicKeyAlgorithmunion type and related validator/converter functionsMLKEM_768 = 'ML-KEM-768'enum entry inlib/src/crypto/enums.tsparsePublicKeyPemand type declarations to detect and handle ML-KEM-768 SPKI keys (OID2.16.840.1.101.3.4.4.2)@noble/post-quantumdependency for pure-JS ML-KEM-768 encapsulationMLKEMWrappedclass with wire format: KAOtype="wrapped",wrappedKey=base64(ml_kem_ciphertext[1088] || aes_kw_wrapped_dek), noephemeralPublicKey'mlkem:768'algorithm throughbuildKeyAccess→MLKEMWrappedxtest/sdk/js/cli.shwithsupports mechanism-mlkemcase for the cross-platform test harnessReferences
Test plan
tsc --noEmit)npm run lint)xtest/sdk/js/cli.sh supports mechanism-mlkemexits 0xtest/scenarios/mechanism-mlkem.yaml(requires platform-service cell)🤖 Generated with Claude Code