Post-quantum-aware network handshake inspector for TLS, SSH, IKE, QUIC, WireGuard, and more
Project description
quantum-sniffer
A network traffic analyzer that captures and classifies cryptographic handshakes from encrypted protocols (TLS, SSH, IPsec, WireGuard, DTLS, QUIC, DoT, STARTTLS, SMB, RDP, Kerberos, SNMPv3, OpenVPN, RADIUS, AMQP, SIP/SIPS, ZRTP, BGP, OPC-UA, and more) and tags each one as post-quantum secure, hybrid, classical, or unknown.
Status: pre-alpha. Public protocols and CLI flags may change.
Install
From source (until published to PyPI):
git clone https://github.com/jfrancis/quantum-sniffer.git
cd quantum-sniffer
pip install .
# or, for development with editable install + tests:
pip install -e '.[dev]'
Requirements: Python 3.9+, scapy. cryptography is optional and unlocks
QUIC Initial-packet decryption.
After install, the quantum-sniffer console script is on $PATH. If
you'd rather not install, every example below works with
python3 -m quantum_sniffer … from the repo root.
Quick reference
quantum-sniffer --help # show all flags
sudo quantum-sniffer -o capture.jsonl -i eth0 # live capture (root required)
quantum-sniffer -o capture.jsonl -r some.pcap # replay a saved pcap (no root)
quantum-sniffer --find-sarah-connor capture.jsonl # Skynet-readiness report
Usage
Live capture
sudo quantum-sniffer -o capture.jsonl -i eth0
Live mode requires root (or cap_net_raw on Linux: sudo setcap cap_net_raw+ep $(readlink -f $(which python3))).
Replay a saved pcap
No root needed — useful for regression testing and analyzing captures collected elsewhere.
quantum-sniffer -o capture.jsonl -r some.pcap
Flags
Required for capture/replay modes:
-o, --output PATH— JSONL log file (one event per line, appended). Not required when only running--find-sarah-connor.
Common flags:
-i, --interface IFACE— capture interface (default: scapy's default)-r, --read FILE.pcap— analyze a saved capture instead of going live-a, --all— include unencrypted protocols (HTTP, plain DNS, etc.)--bpf "tcp port 443"— override the built-in BPF filter--host 10.0.0.5— narrow whichever filter is in effect to one host-q, --quiet— write JSONL without printing each event to the console--debug— re-raise analyzer exceptions instead of logging them--find-sarah-connor CAPTURE.jsonl— see "Skynet readiness report" below--with-skull— adds an ASCII skull to the readiness report
--interface and --read are mutually exclusive.
Post-Quantum Classification
Each event carries a post_quantum_secure field:
| Status | Meaning |
|---|---|
Yes |
Pure post-quantum KEX/signature confirmed |
Hybrid |
Mix of PQ and classical (transition deployment) |
No |
Classical only — vulnerable to harvest-now-decrypt-later |
Unknown |
Couldn't determine from observable handshake bytes |
Classification is driven from explicit (group_id -> classification) tables
(quantum_sniffer/pq.py), not substring matching, so novel hybrid names
can't be silently misclassified.
PQ algorithms tracked include CRYSTALS-Kyber (and the standardized name ML-KEM), x25519/x448 hybrids, sntrup761x25519 (OpenSSH), and IKEv2 ML-KEM DH groups (transform IDs 35–37).
Output Format
JSON Lines, one event per line. Append-only — safe for long-running captures (the previous JSON-array format rewrote the entire file on every packet, which became O(N²) and dropped traffic on busy links).
{"protocol":"TLS","type":"TLS ClientHello","timestamp":"2026-06-10T12:34:56.789",...}
{"protocol":"WireGuard","type":"WireGuard Handshake Initiation",...}
To consume:
# As a single JSON array
jq -s . capture.jsonl
# Filter line-by-line
jq -c 'select(.post_quantum_secure == "Hybrid")' capture.jsonl
# Quantum readiness summary
jq -s 'group_by(.post_quantum_secure) | map({status: .[0].post_quantum_secure, count: length})' capture.jsonl
# CSV export
jq -r '[.timestamp, .protocol, .post_quantum_secure, .connection,
.tls_version // .ssh_protocol_version, .selected_cipher.name // "-"]
| @csv' capture.jsonl > report.csv
What Gets Captured
TLS / DTLS / QUIC: protocol versions, cipher suites, key exchange
groups (including PQ groups like x25519kyber768), SNI, ALPN, ECH presence,
session resumption flags. QUIC Initial packets are decrypted when
cryptography is installed, exposing the inner TLS 1.3 ClientHello.
SSH: banner version, then KEXINIT — full algorithm negotiation lists (KEX, host-key, encryption, MAC).
IPsec/IKEv2: SA proposals walked end-to-end, including PQ DH transform IDs (ML-KEM-512/768/1024).
WireGuard: handshake message types and sizes; oversized handshakes flag possible experimental PQ variants.
Other: STARTTLS upgrades, SMB dialect, RDP/CredSSP negotiation, Kerberos etypes, SNMPv3 security level, OpenVPN control packets, RADIUS codes + EAP method, AMQP banner, SIP/SIPS, ZRTP key agreement, BGP/BGP- over-TLS, OPC-UA security policies, and a heuristic TLS detector for ~30 non-standard ports plus Tor (9001/9030/9050/9051/9150).
Skynet readiness report
--find-sarah-connor reads a JSONL capture and reports how much of the
traffic would be readable by a sufficiently large quantum computer — i.e.,
the harvest-now-decrypt-later exposure surface, in Terminator drag.
quantum-sniffer --find-sarah-connor capture.jsonl
quantum-sniffer --find-sarah-connor capture.jsonl --with-skull
You get:
- Counts and percentages by classification (classical / hybrid / PQ / unknown)
- A ranked list of "high-value targets" — SNIs/IPs whose sessions were classical-only
- Per-protocol breakdown (TLS / WireGuard / SSH / …)
- A verdict that scales with the data ("JUDGMENT DAY IS INEVITABLE" all the way up to "HASTA LA VISTA, BABY")
This mode does not require --output and does no packet capture.
Testing
python3 -m pytest tests/
38 tests cover the bounds-check fixes in the raw TLS parser (truncated session-id / cipher-list / extensions don't crash), PQ classification, SSH KEX parsing, IKEv2 SA parsing (including ML-KEM transform IDs), the JSONL writer, CLI argument handling, and the Skynet report.
Building for PyPI
The repo ships pyproject.toml and MANIFEST.in, so:
pip install --user build twine
python3 -m build # produces dist/*.whl + dist/*.tar.gz
python3 -m twine check dist/* # validates README + metadata
# python3 -m twine upload dist/* # uncomment to actually publish
Limitations
- Cannot decrypt application traffic — handshake metadata only.
- ClientHello fragmentation across TCP segments is detected and flagged but not yet reassembled. PQ key shares often push ClientHello past one segment, so flagged events deserve attention.
- PQ detection only catches algorithms whose IDs/names this tool knows
about. New IANA assignments need updates to
quantum_sniffer/constants.pyandquantum_sniffer/pq.py.
Legal
Authorized use only. Monitoring networks you don't own or lack written permission to monitor likely violates the Computer Fraud and Abuse Act (US), GDPR (EU), or similar laws elsewhere.
License
GNU General Public License v3.0
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 quantum_sniffer-0.2.0.tar.gz.
File metadata
- Download URL: quantum_sniffer-0.2.0.tar.gz
- Upload date:
- Size: 45.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
498f1268c68420270b4511c48871af74fe3ff0cc3a40e0cbe95a680c43c053ed
|
|
| MD5 |
ee1050d2c26d44030853a6d149845993
|
|
| BLAKE2b-256 |
1a2900a8c840d2e384d18ecd2165428494c7fd14a792e95b5cfcc168dfa7740d
|
File details
Details for the file quantum_sniffer-0.2.0-py3-none-any.whl.
File metadata
- Download URL: quantum_sniffer-0.2.0-py3-none-any.whl
- Upload date:
- Size: 45.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6f7591d10df7b3b98f6d3a8a35d384557d484cbef76171c8d8a4a35d8c5a1c2
|
|
| MD5 |
7171279e7d9e3a2e944628015155a770
|
|
| BLAKE2b-256 |
33ea64541f0f022fa540004b9a05eaf523d067ac490cf51a5797d5dd5d51cc7c
|