Skip to content

feat: PQC PKCS#8 seed validation for ML-DSA and ML-KEM#1017

Merged
boorad merged 2 commits into
mainfrom
feat/pqc-pkcs8-seed-validation
May 3, 2026
Merged

feat: PQC PKCS#8 seed validation for ML-DSA and ML-KEM#1017
boorad merged 2 commits into
mainfrom
feat/pqc-pkcs8-seed-validation

Conversation

@boorad
Copy link
Copy Markdown
Collaborator

@boorad boorad commented May 3, 2026

Summary

Closes #997.

Aligns ML-DSA / ML-KEM PKCS#8 import and export with Node's WebCrypto:

  • Import: rejects "seedless" PKCS#8 blobs (priv-only, no seed) with NotSupportedError — identified by exact byte length (matches Node's lib/internal/crypto/ml_dsa.js and ml_kem.js).
  • Export: configures OpenSSL providers to prefer seed-only,priv-only PKCS#8 output (mirrors node/src/crypto/crypto_util.cc) so RNQC emits the compact 54-byte (ML-DSA) / 86-byte (ML-KEM) encoding by default. Adds a defensive length check that throws OperationError if a seedless KeyObject was wrapped via toCryptoKey and slipped past the import guard.

Changes

  • packages/react-native-quick-crypto/src/subtle.ts
    • Export PQC_SEEDLESS_PKCS8_LENGTHS and add PQC_FAMILY lookup; reject seedless PKCS#8 imports with the same error message Node emits.
    • Add post-export length guard for ML-DSA (54) / ML-KEM (86) PKCS#8 in exportKeyPkcs8.
  • packages/react-native-quick-crypto/cpp/keys/HybridKeyObjectHandle.{cpp,hpp}
    • Configure provider ml-{kem,dsa}.output_formats to seed-only,priv-only once, on first HybridKeyObjectHandle construction, via std::call_once. Gated to OpenSSL ≥ 3.6 (OSSL_PROVIDER_add_conf_parameter).
  • example/src/tests/subtle/import_export.ts
    • Seedless-import rejection tests for all six variants.
    • Export-length tests for all six variants.
    • Round-trip + sign/verify (ML-DSA-65) and round-trip + decapsulate (ML-KEM-768).

Test plan

  • subtle: ML-DSA-{44,65,87} pkcs8 import rejects seedless keyNotSupportedError thrown with Node-matching message
  • subtle: ML-KEM-{512,768,1024} pkcs8 import rejects seedless keyNotSupportedError thrown
  • subtle: ML-DSA-{44,65,87} pkcs8 export produces 54-byte seed-only encoding
  • subtle: ML-KEM-{512,768,1024} pkcs8 export produces 86-byte seed-only encoding
  • subtle: ML-DSA-65 pkcs8 round-trip + sign/verify
  • subtle: ML-KEM-768 pkcs8 round-trip + decapsulate

boorad added 2 commits May 3, 2026 16:00
Reject ML-DSA / ML-KEM PKCS#8 imports that contain only the expanded
private key (seedless), and validate exported PKCS#8 length against
22 + seed_size to catch the toCryptoKey-on-seedless-KeyObject edge case.

Also configures OpenSSL providers to prefer seed-only PKCS#8 output for
ML-DSA / ML-KEM (seed-only,priv-only), mirroring Node's
src/crypto/crypto_util.cc. Without this, OpenSSL defaults to seed-priv,
which silently produces a longer encoding and breaks cross-implementation
interop.
- Export PQC_SEEDLESS_PKCS8_LENGTHS and consume it from tests instead
  of redeclaring the table in the test file.
- Replace startsWith fallback with explicit PQC_FAMILY lookup so the
  import-rejection error message can't mislabel an unknown algorithm
  whose seedless length happens to collide.
- Move configurePqcOutputFormats() call into HybridKeyObjectHandle
  ctor (still guarded by std::call_once) so providers are configured
  on first handle construction rather than per-export.
- Add ML-DSA-65 PKCS#8 round-trip + sign/verify test mirroring the
  existing ML-KEM-768 round-trip.
- Replace absolute-path comment with repo-relative reference.
@boorad boorad self-assigned this May 3, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-native-quick-crypto Ready Ready Preview, Comment May 3, 2026 9:31pm

Request Review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

🤖 End-to-End Test Results - Android

Status: ✅ Passed
Platform: Android
Run: 25291377993

📸 Final Test Screenshot

Maestro Test Results - android

Screenshot automatically captured from End-to-End tests and will expire in 30 days


This comment is automatically updated on each test run.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

🤖 End-to-End Test Results - iOS

Status: ✅ Passed
Platform: iOS
Run: 25291377998

📸 Final Test Screenshot

Maestro Test Results - ios

Screenshot automatically captured from End-to-End tests and will expire in 30 days


This comment is automatically updated on each test run.

@boorad boorad merged commit 849029a into main May 3, 2026
10 checks passed
@boorad boorad deleted the feat/pqc-pkcs8-seed-validation branch May 3, 2026 22:28
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.

subtle: validate ML-DSA/ML-KEM PKCS#8 seed presence (import) and length (export)

1 participant