PQC-secured transport layer for Model Context Protocol (MCP). ML-DSA signed messages and agent identity verification.
Project description
PQC MCP Transport
Post-quantum secured transport layer for the Model Context Protocol (MCP). Every JSON-RPC message is signed with ML-DSA (FIPS 204) digital signatures, providing cryptographic authentication, integrity verification, and replay protection that is resistant to both classical and quantum attacks.
Installation
pip install pqc-mcp-transport
For development:
pip install pqc-mcp-transport[dev]
Quick Start
Server
import asyncio
from quantumshield.identity.agent import AgentIdentity
from pqc_mcp_transport import PQCMCPServer
server_identity = AgentIdentity.create("my-server")
server = PQCMCPServer(identity=server_identity)
@server.tool("greet", description="Greet someone")
async def greet(name: str) -> str:
return f"Hello, {name}!"
asyncio.run(server.run(port=8080))
Client
import asyncio
from quantumshield.identity.agent import AgentIdentity
from pqc_mcp_transport import PQCMCPClient
async def main():
agent = AgentIdentity.create("my-client")
client = PQCMCPClient(identity=agent, server_url="http://localhost:8080")
session = await client.connect() # PQC handshake
result = await client.call_tool("greet", {"name": "World"})
print(result) # Verified response
await client.close()
asyncio.run(main())
Architecture
Client Server
------ ------
| |
| 1. HandshakeRequest (signed with ML-DSA) |
|---------------------------------------------->|
| | verify client sig
| 2. HandshakeResponse (signed with ML-DSA) |
|<----------------------------------------------|
| verify server sig |
| |
| === Session Established (mutual auth) === |
| |
| 3. JSON-RPC Request + _pqc envelope |
|---------------------------------------------->|
| verify sig, | check nonce,
| execute tool | sign response
| 4. JSON-RPC Response + _pqc envelope |
|<----------------------------------------------|
| verify response sig |
Protocol Specification
Handshake (Mutual Authentication)
- Client generates a nonce, signs
{did}:{nonce}:{timestamp}with its ML-DSA private key, and sends aHandshakeRequest. - Server verifies the client's signature, generates its own nonce and a session ID, signs
{did}:{client_nonce}:{server_nonce}:{session_id}, and returns aHandshakeResponse. - Client verifies the server's signature and the echoed nonce. A
PQCSessionis created on both sides.
Message Format
Every MCP JSON-RPC message carries a _pqc envelope:
{
"jsonrpc": "2.0",
"method": "tools/call",
"id": "abc123",
"params": { "name": "greet", "arguments": { "name": "World" } },
"_pqc": {
"signer_did": "did:pqaid:abcdef...",
"algorithm": "ML-DSA-65",
"timestamp": "2025-01-15T10:30:00+00:00",
"nonce": "a1b2c3d4e5f6...",
"signature": "3045022100...",
"public_key": "302a300506..."
}
}
The _pqc field is stripped before signing (canonical form) and before passing the message to standard MCP handlers.
Signing Process
- Remove
_pqcfrom the message. - Serialize with
json.dumps(msg, sort_keys=True, separators=(',', ':')). - Compute
SHA3-256hash of the canonical bytes. - Sign the hash with ML-DSA.
- Attach the
_pqcenvelope with signature, public key, nonce, and timestamp.
Security Properties
| Property | Mechanism |
|---|---|
| Message Authentication | Every message is ML-DSA signed |
| Mutual Authentication | Both client and server verify each other during handshake |
| Integrity | Canonical JSON + SHA3-256 hash prevents tampering |
| Replay Protection | Per-session nonce tracking rejects duplicates |
| Session Expiry | Sessions have a configurable TTL (default: 1 hour) |
| Quantum Resistance | ML-DSA (FIPS 204) is resistant to Shor's algorithm |
| Audit Trail | Every operation is logged with signature metadata |
API Reference
MessageSigner
| Method | Description |
|---|---|
canonicalize(message) |
Deterministic JSON serialization (static) |
sign_message(message) |
Add _pqc envelope with ML-DSA signature |
verify_message(message) |
Verify _pqc envelope, returns VerificationResult (static) |
strip_pqc(message) |
Remove _pqc for standard MCP processing (static) |
PQCHandshake
| Method | Description |
|---|---|
initiate(identity) |
Create a signed handshake request |
respond(request, server_identity) |
Verify client, create signed response |
complete(response, client_identity, nonce) |
Verify server, create session |
PQCSession
| Method | Description |
|---|---|
is_valid() |
Check if session has not expired |
check_nonce(nonce) |
Register nonce, raise ReplayAttackError on reuse |
log_operation(...) |
Record operation in audit trail |
get_audit_log() |
Return list of AuditEntry records |
PQCMCPClient
| Method | Description |
|---|---|
connect() |
Perform PQC handshake, return PQCSession |
call_tool(name, arguments) |
Send signed tool call, verify response |
list_tools() |
List available tools (signed request) |
close() |
Close connection |
PQCMCPServer
| Method | Description |
|---|---|
tool(name, description) |
Decorator to register a tool handler |
handle_request(raw_message) |
Process incoming request with PQC verification |
handle_handshake(request) |
Handle handshake initiation |
get_tool_list() |
Return registered tools |
run(host, port) |
Start HTTP server |
PQCMiddleware
ASGI middleware for adding PQC to existing frameworks (Starlette, FastAPI):
from pqc_mcp_transport.middleware import PQCMiddleware
app = PQCMiddleware(app, server_identity=identity)
Exceptions
| Exception | When |
|---|---|
PQCTransportError |
Base exception |
SignatureVerificationError |
Signature did not verify |
HandshakeError |
Handshake failed |
SessionExpiredError |
Session timed out |
ReplayAttackError |
Nonce reused |
PeerNotAuthenticatedError |
No handshake completed |
Examples
See the examples/ directory:
simple_server.py-- Run a PQC MCP server with signed responsessimple_client.py-- Connect to a server with PQC handshakemutual_auth.py-- In-memory mutual authentication demo
Development
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Lint
ruff check src/ tests/
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Write tests for your changes
- Ensure all tests pass (
pytest) - Submit a pull request
License
Apache License 2.0. See LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pqc_mcp_transport-0.1.0.tar.gz.
File metadata
- Download URL: pqc_mcp_transport-0.1.0.tar.gz
- Upload date:
- Size: 17.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9067744c995d1ea2399cf91f03dd1e8a838360973d41b25b9d3a5a25e1008124
|
|
| MD5 |
2fdade53d151dc8f4e64a680ad8d2c63
|
|
| BLAKE2b-256 |
ce013732463ba001eaed6b061b9b46ace5bd70719d574894785d505461298ff6
|
File details
Details for the file pqc_mcp_transport-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pqc_mcp_transport-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1880bf15f2f3887620ed2ded41685c56bf9f4ea826cdb2fac84282bb67341c68
|
|
| MD5 |
75cac9ef52996f3ae31d74b80c3797b1
|
|
| BLAKE2b-256 |
f7c825f13ec119062270bb196296126518a8a9b38e9a718729039a18090cd3be
|