Skip to main content

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 analysis
  • capture.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 .csv and .jsonl added automatically). Defaults to quantum-log if 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_secure
  • src_ip, src_port, dst_ip, dst_port, connection, direction
  • encrypted, tls_version, server_name, selected_cipher_name
  • ssh_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.py and quantum_sniffer/pq.py

Active Probing

  • TLS/HTTPS: Python's ssl module doesn't expose negotiated key exchange groups. Result: TLS 1.3 connections classified as "Unknown" (can't see which group was used). Future: Use cryptography library 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quantum_sniffer-0.4.0.tar.gz (82.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

quantum_sniffer-0.4.0-py3-none-any.whl (76.7 kB view details)

Uploaded Python 3

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

Hashes for quantum_sniffer-0.4.0.tar.gz
Algorithm Hash digest
SHA256 c4df7bc34deeb3ef181b2134851fd0799f69690ffd24b8d73de4e81b01f4372e
MD5 62c73d3e50dc68f197961d5028a780b1
BLAKE2b-256 861b00e9cc338c80c85c6c905d9a5d0faff85147fed4cea1bd4b8dbc69e8d1a6

See more details on using hashes here.

File details

Details for the file quantum_sniffer-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for quantum_sniffer-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cd66dcc4cda10b501c1d1c3249512e8e1f7a605ad8526f6a8c0cbbaa3dc03510
MD5 976f43e5148a3ac63e09c5facb1e5e04
BLAKE2b-256 eecce295ce6551d4f0658fe00e6c6211fd0ab3564eabf2444abcfc6ae7a17723

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page