[PR #37] Add post-quantum cryptography support with ML-DSA-65 #38

Open
opened 2026-02-28 01:18:07 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/therealpaulgg/ssh-sync-server/pull/37
Author: @therealpaulgg
Created: 2/13/2026
Status: 🔄 Open

Base: mainHead: claude/quantum-resistant-key-generation-XTHBx


📝 Commits (9)

  • 6e371a5 Add quantum-resistant ML-DSA-65 JWT verification and key migration support
  • c4e4131 debugging updates
  • 6936a16 start using shared library for my sanity
  • a3cccd7 remove go work
  • 83d5435 Pivot to hybrid ECDH P-256 + ML-KEM-768 key exchange scheme
  • 98355f2 Revert "Pivot to hybrid ECDH P-256 + ML-KEM-768 key exchange scheme"
  • e51e88f no hybrid crypto, MLDSA65 -> MLDSA
  • a3d5ebc PR comments
  • 2ff252e deslop

📊 Changes

21 files changed (+968 additions, -171 deletions)

View changed files

📝 Dockerfile (+1 -1)
Dockerfile.debug (+24 -0)
📝 docker-compose.yaml (+1 -1)
📝 go.mod (+8 -26)
📝 go.sum (+8 -52)
pkg/crypto/pqc.go (+151 -0)
pkg/crypto/pqc_test.go (+193 -0)
📝 pkg/database/repository/machine.go (+11 -0)
📝 pkg/database/repository/machinemock.go (+14 -0)
📝 pkg/web/live/main.go (+31 -22)
📝 pkg/web/middleware/auth.go (+62 -21)
📝 pkg/web/middleware/auth_test.go (+164 -28)
📝 pkg/web/router/routes/data.go (+1 -1)
📝 pkg/web/router/routes/data_test.go (+1 -1)
📝 pkg/web/router/routes/machine.go (+42 -1)
📝 pkg/web/router/routes/machine_test.go (+108 -1)
📝 pkg/web/router/routes/setup.go (+7 -14)
📝 pkg/web/router/routes/setup_test.go (+63 -0)
📝 pkg/web/router/routes/user.go (+1 -1)
📝 pkg/web/router/routes/user_test.go (+1 -1)

...and 1 more files

📄 Description

Summary

Adds post-quantum cryptography support using ML-DSA (FIPS 204, MLDSA65 parameter set) for JWT authentication alongside existing ECDSA. The server can now accept, validate, and verify ML-DSA-signed JWTs while maintaining full backward compatibility.

Key Changes

New PQC crypto package (pkg/crypto/pqc.go)

  • Uses filippo.io/mldsa library (not Cloudflare CIRCL)
  • DetectKeyType(): Identifies key type from PEM block ("MLDSA PUBLIC KEY" vs EC)
  • ValidatePublicKey(): Validates both ECDSA and ML-DSA public keys
  • ParseMLDSAPublicKey(): Parses ML-DSA public key from PEM
  • DetectJWTAlgorithm(): Extracts algorithm from JWT header
  • ExtractJWTClaims(): Manually parses JWT claims (for algorithms unsupported by lestrrat-go/jwx)
  • VerifyMLDSAJWT(): Verifies ML-DSA signatures and token expiration

Authentication middleware (pkg/web/middleware/auth.go)

  • Algorithm-aware routing: ES256/ES512 → lestrrat-go/jwx; MLDSA → manual verification
  • JWT algorithm header identifier: "MLDSA" (matches the filippo.io/mldsa parameter set naming)

Machine key management (pkg/web/router/routes/machine.go)

  • New PUT /api/v1/machines/key endpoint for updating a machine's public key
  • Validates uploaded keys via ValidatePublicKey() (supports both ECDSA and ML-DSA)

Setup route (pkg/web/router/routes/setup.go)

  • Uses ValidatePublicKey() during machine registration to accept both key types

Repository (pkg/database/repository/machine.go)

  • Added UpdateMachinePublicKey(id, publicKey) to MachineRepository interface and implementation
  • Machine model stores a single PublicKey []byte (no separate encapsulation key)

WebSocket challenge flow (pkg/web/live/main.go)

  • Validates incoming public key with pqc.ValidatePublicKey() before storing

Test utilities (pkg/web/testutils/main.go)

  • GenerateMLDSATestKeys(): Creates ML-DSA keypairs for testing
  • EncodeMLDSAToPem(): PEM-encodes ML-DSA public keys ("MLDSA PUBLIC KEY" block type)
  • GenerateMLDSATestToken() / GenerateExpiredMLDSATestToken(): Creates signed ML-DSA JWTs

Test coverage

  • pkg/crypto/pqc_test.go: Tests for key detection, parsing, JWT algorithm detection, claims extraction, and signature verification
  • Updated auth middleware tests: valid ML-DSA token, wrong key, expired token
  • Machine route tests for key update endpoint
  • Setup route test for ML-DSA key registration

Implementation Notes

  • PEM block type: "MLDSA PUBLIC KEY" for clear identification
  • ECDSA verification unchanged; lestrrat-go/jwx still used for ES256/ES512 paths
  • Full backward compatibility: existing ECDSA-based clients continue to work without changes

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/therealpaulgg/ssh-sync-server/pull/37 **Author:** [@therealpaulgg](https://github.com/therealpaulgg) **Created:** 2/13/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `claude/quantum-resistant-key-generation-XTHBx` --- ### 📝 Commits (9) - [`6e371a5`](https://github.com/therealpaulgg/ssh-sync-server/commit/6e371a5d0741eb45c40a8eb0853aef70abed2bad) Add quantum-resistant ML-DSA-65 JWT verification and key migration support - [`c4e4131`](https://github.com/therealpaulgg/ssh-sync-server/commit/c4e413109164d829ed7084c9b00483009ef19fea) debugging updates - [`6936a16`](https://github.com/therealpaulgg/ssh-sync-server/commit/6936a162da85f3bfbac41dc2c4e5384def754711) start using shared library for my sanity - [`a3cccd7`](https://github.com/therealpaulgg/ssh-sync-server/commit/a3cccd7b24493b824e11b8b105efc1f7791b16cc) remove go work - [`83d5435`](https://github.com/therealpaulgg/ssh-sync-server/commit/83d543527fbbe9a94f56937b53d8db2f22cb728a) Pivot to hybrid ECDH P-256 + ML-KEM-768 key exchange scheme - [`98355f2`](https://github.com/therealpaulgg/ssh-sync-server/commit/98355f219f72a21cd6579fc52a01c89ebb6bb624) Revert "Pivot to hybrid ECDH P-256 + ML-KEM-768 key exchange scheme" - [`e51e88f`](https://github.com/therealpaulgg/ssh-sync-server/commit/e51e88f937606a9a7f74f066089f46ebd2fa5459) no hybrid crypto, MLDSA65 -> MLDSA - [`a3d5ebc`](https://github.com/therealpaulgg/ssh-sync-server/commit/a3d5ebc58dfdd0b7d7f0f910b5025bb1ada3d2ba) PR comments - [`2ff252e`](https://github.com/therealpaulgg/ssh-sync-server/commit/2ff252e2aaf3b0c874429c3b58dff26f81ccfbca) deslop ### 📊 Changes **21 files changed** (+968 additions, -171 deletions) <details> <summary>View changed files</summary> 📝 `Dockerfile` (+1 -1) ➕ `Dockerfile.debug` (+24 -0) 📝 `docker-compose.yaml` (+1 -1) 📝 `go.mod` (+8 -26) 📝 `go.sum` (+8 -52) ➕ `pkg/crypto/pqc.go` (+151 -0) ➕ `pkg/crypto/pqc_test.go` (+193 -0) 📝 `pkg/database/repository/machine.go` (+11 -0) 📝 `pkg/database/repository/machinemock.go` (+14 -0) 📝 `pkg/web/live/main.go` (+31 -22) 📝 `pkg/web/middleware/auth.go` (+62 -21) 📝 `pkg/web/middleware/auth_test.go` (+164 -28) 📝 `pkg/web/router/routes/data.go` (+1 -1) 📝 `pkg/web/router/routes/data_test.go` (+1 -1) 📝 `pkg/web/router/routes/machine.go` (+42 -1) 📝 `pkg/web/router/routes/machine_test.go` (+108 -1) 📝 `pkg/web/router/routes/setup.go` (+7 -14) 📝 `pkg/web/router/routes/setup_test.go` (+63 -0) 📝 `pkg/web/router/routes/user.go` (+1 -1) 📝 `pkg/web/router/routes/user_test.go` (+1 -1) _...and 1 more files_ </details> ### 📄 Description ## Summary Adds post-quantum cryptography support using **ML-DSA** (FIPS 204, MLDSA65 parameter set) for JWT authentication alongside existing ECDSA. The server can now accept, validate, and verify ML-DSA-signed JWTs while maintaining full backward compatibility. ## Key Changes ### New PQC crypto package (`pkg/crypto/pqc.go`) - Uses `filippo.io/mldsa` library (not Cloudflare CIRCL) - `DetectKeyType()`: Identifies key type from PEM block (`"MLDSA PUBLIC KEY"` vs EC) - `ValidatePublicKey()`: Validates both ECDSA and ML-DSA public keys - `ParseMLDSAPublicKey()`: Parses ML-DSA public key from PEM - `DetectJWTAlgorithm()`: Extracts algorithm from JWT header - `ExtractJWTClaims()`: Manually parses JWT claims (for algorithms unsupported by lestrrat-go/jwx) - `VerifyMLDSAJWT()`: Verifies ML-DSA signatures and token expiration ### Authentication middleware (`pkg/web/middleware/auth.go`) - Algorithm-aware routing: `ES256`/`ES512` → lestrrat-go/jwx; `MLDSA` → manual verification - JWT algorithm header identifier: `"MLDSA"` (matches the filippo.io/mldsa parameter set naming) ### Machine key management (`pkg/web/router/routes/machine.go`) - New `PUT /api/v1/machines/key` endpoint for updating a machine's public key - Validates uploaded keys via `ValidatePublicKey()` (supports both ECDSA and ML-DSA) ### Setup route (`pkg/web/router/routes/setup.go`) - Uses `ValidatePublicKey()` during machine registration to accept both key types ### Repository (`pkg/database/repository/machine.go`) - Added `UpdateMachinePublicKey(id, publicKey)` to `MachineRepository` interface and implementation - Machine model stores a single `PublicKey []byte` (no separate encapsulation key) ### WebSocket challenge flow (`pkg/web/live/main.go`) - Validates incoming public key with `pqc.ValidatePublicKey()` before storing ### Test utilities (`pkg/web/testutils/main.go`) - `GenerateMLDSATestKeys()`: Creates ML-DSA keypairs for testing - `EncodeMLDSAToPem()`: PEM-encodes ML-DSA public keys (`"MLDSA PUBLIC KEY"` block type) - `GenerateMLDSATestToken()` / `GenerateExpiredMLDSATestToken()`: Creates signed ML-DSA JWTs ### Test coverage - `pkg/crypto/pqc_test.go`: Tests for key detection, parsing, JWT algorithm detection, claims extraction, and signature verification - Updated auth middleware tests: valid ML-DSA token, wrong key, expired token - Machine route tests for key update endpoint - Setup route test for ML-DSA key registration ## Implementation Notes - PEM block type: `"MLDSA PUBLIC KEY"` for clear identification - ECDSA verification unchanged; lestrrat-go/jwx still used for ES256/ES512 paths - Full backward compatibility: existing ECDSA-based clients continue to work without changes --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/ssh-sync-server#38
No description provided.