Post-quantum-aware network handshake inspector for TLS, SSH, IKE, QUIC, WireGuard, and more
Project description
quantum-sniffer
A network traffic analyzer and active prober that captures and classifies cryptographic handshakes from encrypted protocols (TLS, SSH, IPsec, QUIC, WireGuard, and more) and tags each one as post-quantum secure, hybrid, classical, or unknown.
Status: alpha. Public protocols and CLI flags may change.
Features
Passive Analysis (Packet Capture)
- Live capture or pcap replay with full protocol analysis
- Supported protocols: TLS, DTLS, QUIC, SSH, IPsec/IKEv2, WireGuard, DNS-over-TLS, DNSSEC, STARTTLS variants, SMB, RDP, Kerberos, SNMPv3, OpenVPN, RADIUS, AMQP, SIP/SIPS, ZRTP, BGP, OPC-UA
- Dual output: CSV (spreadsheet-friendly) + JSONL (complete event data)
- Skynet report: Harvest-now-decrypt-later exposure analysis
Active Probing (NEW in v0.4.0)
- Test targets for post-quantum crypto support without waiting for traffic
- Auto-detection: Probes TLS, SSH, STARTTLS (SMTP/IMAP/POP3/FTP), IKEv2 based on port
- Bulk scanning: CIDR subnets, IP ranges, comma-separated lists
- Parallel probing: Configurable workers for fast scanning
- Rich output: JSON with metadata, Markdown reports, stdout display
- SNI support: Works with virtual hosting and name-based servers
Install
From PyPI (recommended):
pip install quantum-sniffer
# To upgrade an existing installation:
pip install --upgrade quantum-sniffer
From source (for development):
git clone https://github.com/illumio-community/quantum-sniffer.git
cd quantum-sniffer
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 Start
Passive Capture
# Live capture (creates .csv + .jsonl)
sudo quantum-sniffer -o capture -i eth0
# Replay a saved pcap
quantum-sniffer -o capture -r some.pcap
# Skynet readiness report
quantum-sniffer --find-sarah-connor capture.jsonl
Active Probing
# Probe single target
quantum-sniffer --probe example.com
# Probe specific ports
quantum-sniffer --probe 10.1.1.100 --ports 22,443
# Probe subnet with JSON output
quantum-sniffer --probe 10.1.1.0/24 --ports 22,443 \
--output-json scan-results.json \
--output-markdown scan-report.md
# Probe IP range
quantum-sniffer --probe 192.168.1.1-50 --ports 443 --workers 20
# Probe multiple specific IPs
quantum-sniffer --probe 10.1.1.10,10.1.1.20,10.1.1.30 --ports 22,443
Usage
Passive Capture Mode
Live capture:
sudo quantum-sniffer -o capture -i eth0
This creates two files:
capture.csv— flattened data (17 columns) for spreadsheet analysiscapture.jsonl— complete event data with nested structures
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 -r some.pcap
Common flags:
-o, --output PATH— base filename for output logs (extensions.csvand.jsonladded automatically). Defaults toquantum-logif not specified. Not used by--find-sarah-connor.-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.
Active Probing Mode
Basic probing:
# Probe single target (auto-detects protocols on default ports)
quantum-sniffer --probe example.com
# Probe specific port
quantum-sniffer --probe example.com:443
# Probe custom ports
quantum-sniffer --probe 10.1.1.100 --ports 22,443,25,587
Bulk scanning:
# CIDR subnet
quantum-sniffer --probe 10.1.1.0/24 --ports 443
# IP range (full)
quantum-sniffer --probe 10.1.1.1-10.1.1.50 --ports 22,443
# IP range (shorthand - same first 3 octets)
quantum-sniffer --probe 10.1.1.1-50 --ports 443
# Comma-separated list
quantum-sniffer --probe 10.1.1.10,10.1.1.20,10.1.1.30 --ports 22,443
Output options:
# JSON output with full metadata
quantum-sniffer --probe 10.1.1.0/24 --ports 443 \
--output-json results.json
# Markdown report
quantum-sniffer --probe example.com --ports 22,443,25 \
--output-markdown report.md
# Both formats
quantum-sniffer --probe 10.1.1.0/24 --ports 22,443 \
--output-json results.json \
--output-markdown report.md \
--workers 20 \
--timeout 3
Probing flags:
--probe TARGET— active probe mode (supports: single IP, hostname, CIDR, range, list)--ports PORT,PORT,...— ports to probe (default: auto-detect common encrypted ports)--timeout SECONDS— connection timeout (default: 5.0)--workers N— parallel probe workers for bulk scans (default: 10)--output-json FILE— save results as JSON with metadata--output-markdown FILE— save results as Markdown report
Supported probe protocols:
- TLS/HTTPS (ports 443, 8443, 636, 853, 989, 990, 992, 993, 995, 5061, etc.)
- SSH (port 22) - Excellent PQ detection (KEX algorithms visible)
- STARTTLS-SMTP (ports 25, 587) - Upgrades connection then analyzes TLS
- STARTTLS-IMAP (port 143)
- STARTTLS-POP3 (port 110)
- STARTTLS-FTP (port 21)
- IKEv2/IPsec (ports 500, 4500) - Basic probe (simplified)
Protocol is auto-detected based on port number.
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 Formats
Passive Capture Outputs
CSV Format — Spreadsheet-friendly with 17 core columns:
timestamp,protocol,type,post_quantum_securesrc_ip,src_port,dst_ip,dst_port,connection,directionencrypted,tls_version,server_name,selected_cipher_namessh_banner,application,note
Perfect for filtering/sorting in Excel, LibreOffice Calc, or csvkit.
# Quick analysis in spreadsheet
libreoffice capture.csv
# Command-line filtering
csvgrep -c post_quantum_secure -m "No" capture.csv | csvlook
JSONL Format — Complete event data with nested structures. One JSON object per line, append-only — safe for long-running captures.
{"protocol":"TLS","type":"TLS ClientHello","timestamp":"2026-06-24T12:34:56.789",...,"supported_groups":["x25519kyber768","x25519"],...}
{"protocol":"SSH","type":"SSH KEXINIT","timestamp":"2026-06-24T12:34:57.123",...,"ssh_kex_algorithms":["sntrup761x25519-sha512@openssh.com",...],...}
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
# Extract specific fields
jq -r '[.timestamp, .protocol, .supported_groups[]] | @csv' capture.jsonl
Active Probing Outputs
Console Output — Human-readable summary:
================================================================================
PROBE RESULTS
================================================================================
✓ 10.1.1.100:22 open 🔒 Hybrid
✓ 10.1.1.100:443 open ⚠️ No TLSv1.3, TLS_AES_256_GCM_SHA384
✗ 10.1.1.100:8443 closed
⏱ 10.1.1.100:9443 timeout (Connection timeout (5.0s))
================================================================================
Summary: 2/4 ports open
1/2 with PQ crypto support
================================================================================
JSON Output — Complete scan data with metadata:
{
"metadata": {
"scan_info": {
"source_hostname": "scanner.local",
"source_ip": "10.0.0.5",
"target": "10.1.1.0/24",
"ports_scanned": [22, 443],
"timeout_seconds": 5.0,
"command_line": "quantum-sniffer --probe 10.1.1.0/24 --ports 22,443"
},
"timing": {
"start_time": "2026-06-24T12:00:00.000000",
"end_time": "2026-06-24T12:05:23.456789",
"duration_seconds": 323.457
}
},
"summary": {
"total_ports_scanned": 512,
"open_ports": 48,
"pq_capable_ports": 12
},
"results": [...]
}
Markdown Output — Formatted report with tables and sections:
# Quantum-Sniffer Probe Report
## Scan Information
**Source Hostname**: scanner.local
**Target**: 10.1.1.0/24
...
## Summary
- **Total Ports Scanned**: 512
- **Open**: 48
- **PQ-Capable**: 12/48
## Results
### Open Ports
| Port | Status | TLS Version | Cipher Suite | PQ Status |
|------|--------|-------------|--------------|-----------|
| 22 | open | N/A | N/A | ✓ Hybrid |
| 443 | open | TLSv1.3 | TLS_AES_... | ✗ No |
...
Library Usage
All functionality is available as a Python library:
Passive Analysis
from quantum_sniffer.lib import ProtocolAnalyzer
from scapy.all import rdpcap
# Analyze packets
analyzer = ProtocolAnalyzer(encrypted_only=True)
packets = rdpcap("capture.pcap")
for pkt in packets:
result = analyzer.process(pkt)
if result:
print(f"{result.protocol}: {result.post_quantum_secure}")
# Get summary
summary = analyzer.summary()
print(f"Total events: {summary['events']}")
print(f"PQ status: {summary['post_quantum']}")
Active Probing
from quantum_sniffer.lib import probe_target
from quantum_sniffer.lib.prober import generate_json_report, save_report
# Probe a target
results = probe_target("10.1.1.100", ports=[22, 443], timeout=5.0)
for r in results:
if r.status.value == "open":
print(f"Port {r.target_port}: {r.post_quantum_secure}")
if r.protocol == "ssh":
print(f" SSH KEX: {r.extras.get('ssh_kex_algorithms', [])[:3]}")
elif r.protocol == "tls":
print(f" TLS: {r.tls_version}, {r.cipher_suite}")
# Probe subnet with progress
def show_progress(done, total):
print(f"\rProgress: {done}/{total}", end="", flush=True)
results = probe_target(
"10.1.1.0/24",
ports=[443],
max_workers=20,
timeout=3.0,
progress_callback=show_progress
)
# Generate and save report
json_report = generate_json_report(
results=results,
target="10.1.1.0/24",
ports=[443],
timeout=3.0,
start_time="2026-06-24T12:00:00",
end_time="2026-06-24T12:05:00",
duration_seconds=300.0
)
save_report(json_report, "scan-results.json")
PQ Classification
from quantum_sniffer.lib.pq import classify_tls_group, classify_ssh_kex
# Classify TLS groups
status = classify_tls_group(0x11ec) # x25519kyber768 -> 'hybrid'
# Classify SSH KEX
status = classify_ssh_kex("sntrup761x25519-sha512@openssh.com") # -> 'pq'
What Gets Captured/Probed
Passive Capture
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.
Active Probing
TLS/HTTPS: Full TLS handshake, extracts version/cipher/certificate. SNI support for virtual hosting. Limitation: Python ssl module doesn't expose negotiated groups (TLS 1.3 classified as "Unknown").
SSH: Banner exchange + KEXINIT negotiation. Extracts full KEX algorithm list from server. Excellent PQ detection - algorithms visible in plaintext. Example: GitHub correctly detected as Hybrid (sntrup761x25519).
STARTTLS: Upgrades SMTP/IMAP/POP3/FTP connections to TLS, then analyzes like TLS/HTTPS.
IKEv2: Sends IKE_SA_INIT request, parses response. Simplified - detects IKE but doesn't fully parse proposals yet.
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.
Examples
Security Audit
# Scan your infrastructure for PQ support
quantum-sniffer --probe 10.0.0.0/16 --ports 22,443 \
--output-json pq-audit-2026-06-24.json \
--output-markdown pq-audit-2026-06-24.md \
--workers 50 \
--timeout 3
# Find quantum-vulnerable services
jq -r '.results[] | select(.status == "open" and .post_quantum_secure == "No") | "\(.target_ip):\(.target_port) - \(.protocol)"' pq-audit-2026-06-24.json
Protocol-Specific Scanning
# SSH servers only
quantum-sniffer --probe 10.1.1.0/24 --ports 22
# Web servers
quantum-sniffer --probe servers.txt --ports 443,8443
# Mail servers (STARTTLS)
quantum-sniffer --probe mail.example.com --ports 25,587,143,110
Monitoring
# Passive monitoring
sudo quantum-sniffer -o daily-$(date +%Y%m%d) -i eth0
# Generate report
quantum-sniffer --find-sarah-connor daily-*.jsonl > daily-report.txt
# Active verification
quantum-sniffer --probe critical-servers.txt --ports 22,443 \
--output-json daily-probe-$(date +%Y%m%d).json
Testing
python3 -m pytest tests/
45 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 dual CSV/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
Passive Capture
- 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
Active Probing
- TLS/HTTPS: Python's
sslmodule doesn't expose negotiated key exchange groups. Result: TLS 1.3 connections classified as "Unknown" (can't see which group was used). Future: Usecryptographylibrary to craft custom ClientHello. - IKEv2: Simplified implementation - detects IKE response but doesn't fully parse proposals/transforms yet. Future: Complete DH group extraction.
- No support for: QUIC (complex UDP), RDP/Kerberos (need auth), WireGuard (no negotiation), LDAP STARTTLS (needs ASN.1 encoding)
Legal
Authorized use only.
- Passive monitoring: Monitor networks you own or have permission to monitor
- Active probing: Probe only systems you own or have written permission to test
Port scanning without authorization likely violates the Computer Fraud and Abuse Act (US), GDPR (EU), Computer Misuse Act (UK), or similar laws elsewhere. Always:
- Use reasonable timeouts and rate limiting
- Respect robots.txt and security.txt
- Document authorization in writing
- Comply with local laws
Documentation
- README.md (this file) - Getting started and usage
- PROBING.md - Complete active probing documentation
- REFACTORING_SUMMARY.md - Library architecture details
- example_library_usage.py - Passive analysis examples
- example_probing.py - Active probing examples
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.4.0.tar.gz.
File metadata
- Download URL: quantum_sniffer-0.4.0.tar.gz
- Upload date:
- Size: 82.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c4df7bc34deeb3ef181b2134851fd0799f69690ffd24b8d73de4e81b01f4372e
|
|
| MD5 |
62c73d3e50dc68f197961d5028a780b1
|
|
| BLAKE2b-256 |
861b00e9cc338c80c85c6c905d9a5d0faff85147fed4cea1bd4b8dbc69e8d1a6
|
File details
Details for the file quantum_sniffer-0.4.0-py3-none-any.whl.
File metadata
- Download URL: quantum_sniffer-0.4.0-py3-none-any.whl
- Upload date:
- Size: 76.7 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 |
cd66dcc4cda10b501c1d1c3249512e8e1f7a605ad8526f6a8c0cbbaa3dc03510
|
|
| MD5 |
976f43e5148a3ac63e09c5facb1e5e04
|
|
| BLAKE2b-256 |
eecce295ce6551d4f0658fe00e6c6211fd0ab3564eabf2444abcfc6ae7a17723
|