Skip to content

feat: JCS canonicalization + post-quantum dual-signature (Ed25519 + ML-DSA-65)#7

Open
HaraldeRoessler wants to merge 1 commit intoMoltyCel:mainfrom
HaraldeRoessler:feat/jcs-pqc-dual-signature
Open

feat: JCS canonicalization + post-quantum dual-signature (Ed25519 + ML-DSA-65)#7
HaraldeRoessler wants to merge 1 commit intoMoltyCel:mainfrom
HaraldeRoessler:feat/jcs-pqc-dual-signature

Conversation

@HaraldeRoessler
Copy link
Copy Markdown
Contributor

Summary

Two tightly related changes combined into one PR to avoid merge conflicts:

1. RFC 8785 JCS canonicalization — replaces json.dumps(sort_keys=True) with proper jcs.canonicalize() for deterministic VC serialization

2. Post-quantum dual-signature — every credential signed with both Ed25519 (64 bytes) and ML-DSA-65/Dilithium3 (3,309 bytes) when configured

Zero-risk deployment

If DILITHIUM_PRIVATE_KEY_HEX is not set, the system behaves exactly as before (Ed25519-only). Enable PQC by generating keys:

pip install liboqs-python
python scripts/generate_dilithium_keys.py
export DILITHIUM_PRIVATE_KEY_HEX=...
export DILITHIUM_PUBLIC_KEY_HEX=...

Credential format

Without Dilithium (backward compatible):

{"proof": {"type": "Ed25519Signature2020", "canonicalizationAlgorithm": "JCS", ...}}

With Dilithium (dual-signature):

{"proof": [
  {"type": "Ed25519Signature2020", "verificationMethod": "...#key-ed25519", ...},
  {"type": "DilithiumSignature2026", "verificationMethod": "...#key-dilithium", ...}
]}

New files

File Purpose
app/crypto/dilithium.py ML-DSA-65 key management, sign, verify (KMS + env fallback)
app/crypto/hybrid.py dual_sign() and verify_proof() for all proof formats
scripts/generate_dilithium_keys.py One-time keypair generation

Modified files

File Change
app/credentials.py Uses dual_sign(), verify_proof() handles legacy + dual + future formats
app/swarm/endorsement.py Endorsement VCs use dual_sign()
app/main.py DID document includes Dilithium key when configured, proof list handling

Locally tested

  • Ed25519-only: works, all smoke tests pass
  • Dual-signature: both proofs verified (Ed25519: valid, Dilithium: valid)
  • DID document: 3 keys (#key-ed25519, #key-1 legacy, #key-dilithium)
  • Legacy credentials still verify correctly

Supersedes PRs #5 and #6

Replaces #5 (JCS only) and #6 (PQC only) which would have caused merge conflicts.

Merge order

This PR is independent but recommended to merge after #1 (which adds jcs to requirements.txt).

Generated with Claude Code

…L-DSA-65)

Two related changes combined to avoid merge conflicts:

1. RFC 8785 JCS canonicalization for VC signing
   Replace json.dumps(sort_keys=True) with jcs.canonicalize() for
   deterministic serialization. Verification supports both JCS (new)
   and sort_keys (legacy) via canonicalizationAlgorithm field in proof.

2. Post-quantum dual-signature with ML-DSA-65 (Dilithium3)
   Every credential is signed with both Ed25519 and ML-DSA-65 when
   Dilithium keys are configured. Falls back to Ed25519-only if not.

New files:
- app/crypto/dilithium.py: ML-DSA-65 key management, sign, verify
- app/crypto/hybrid.py: dual_sign() and verify_proof()
- scripts/generate_dilithium_keys.py: keypair generation utility

Modified files:
- app/credentials.py: uses dual_sign(), verify_proof() handles all formats
- app/swarm/endorsement.py: endorsement VCs use dual_sign()
- app/main.py: DID document dynamically includes Dilithium key,
  proof list handling for DB inserts

Tested locally with docker-compose:
- Ed25519-only mode (no Dilithium keys): works, backward compatible
- Dual-signature mode: Ed25519 (64B) + Dilithium (3,309B) both verified
- DID document shows both keys when Dilithium configured
- Legacy single-proof credentials still verify correctly

Migration path:
  Phase 1 (this PR): dual-signature, Ed25519 + ML-DSA-65
  Phase 2 (future): ML-DSA-65 first, Ed25519 deprecated
  Phase 3 (future): Ed25519 sunset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@HaraldeRoessler
Copy link
Copy Markdown
Contributor Author

Merge order

Recommended to merge last — biggest change, includes JCS + PQC. Independent of #2 and #4 but benefits from #1 (which adds jcs to requirements.txt).

Sequence: #1#3#2#4#7

Supersedes closed PRs #5 and #6.

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