Probe a server's TLS configuration and post-quantum (ML-KEM) key-exchange readiness
Project description
pqcprobe
A small command-line TLS probing utility that uses pyOpenSSL to inspect an HTTPS server's TLS configuration, with a focus on post-quantum readiness.
Features:
- Reports negotiated TLS version, cipher, ALPN and certificate summary
- Reports the negotiated key-exchange group and flags whether it is
post-quantum (e.g.
X25519MLKEM768) or classical - Assesses post-quantum posture: enumerates which key-exchange groups the server accepts and flags "harvest-now, decrypt-later" (HNDL) risk when no post-quantum key exchange is offered
- Verifies the certificate matches the requested hostname (SAN/CN, wildcard and IP aware)
- Probes server support for TLS 1.3 and TLS 1.2
- Samples which TLS 1.2 ciphers the server accepts (and attempts TLS 1.3 ciphersuites where supported by OpenSSL)
- Can fetch raw PEM for the server certificate (--raw-cert)
- Concurrency option for probing (--concurrency)
- Human-friendly summary (--pretty) or JSON output (--json)
- Meaningful exit codes for scripting (see below)
Requirements:
- Python 3.9.2+
- pyOpenSSL, cryptography (installed automatically)
- OpenSSL 3.x recommended (3.5+ for post-quantum group support). On macOS the
system
opensslis LibreSSL and cannot test the ML-KEM hybrid groups; install OpenSSL 3.5+ (e.g. via Homebrew) for full functionality.
Install
pip install pqcprobe
This installs a pqcprobe command. To run from a source checkout instead:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -e .
Usage
pqcprobe https://example.com --pretty
pqcprobe example.com:443 --json
pqcprobe example.com:443 --raw-cert
# Post-quantum audit: fail (exit 3) if the server offers no PQC key exchange
pqcprobe https://example.com --fail-on-classical-only
# Skip group probing entirely (e.g. when the openssl CLI is unavailable)
pqcprobe https://example.com --no-groups
(From a source checkout without installing, use python3 pqcprobe.py ....)
Post-quantum key-exchange probing:
- Enumerating group support forces individual groups via the native
opensslCLI, since pyOpenSSL does not expose a way to set the group list. OpenSSL 3.5+ is required for the ML-KEM hybrid groups (X25519MLKEM768, etc.). Groups the local openssl doesn't recognize are reported as "not testable" rather than "unsupported" (relevant on macOS, whose system openssl is LibreSSL). - Reading the negotiated group uses pyOpenSSL's
Connection.get_group_name()and needs no external tools.
Exit codes:
0success1handshake failed2certificate hostname mismatch (when verifying)3no post-quantum key exchange offered (only with--fail-on-classical-only)
Notes:
- Programmatic overriding of TLS 1.3 ciphersuites requires a recent OpenSSL + pyOpenSSL exposing
set_ciphersuites. - Cipher probing may produce handshake failures for many ciphers — the tool records successes and errors.
- Only use pqcprobe against systems you own or are authorized to test.
Contributing
Contributions are welcome — see CONTRIBUTING.md. Security issues should be reported privately per SECURITY.md.
License
MIT © Andre Van Klaveren
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 pqcprobe-0.1.0.tar.gz.
File metadata
- Download URL: pqcprobe-0.1.0.tar.gz
- Upload date:
- Size: 25.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac5739f63fcb7de031b1cf08b390599ff3b8fd8739bfc4464644451b944c2772
|
|
| MD5 |
4e0493a04966265f1df84e5375aa502c
|
|
| BLAKE2b-256 |
40860c1a532ec02a0bde78b19129f2ee2eb7f5134793f4b39390c4db496315ae
|
Provenance
The following attestation bundles were made for pqcprobe-0.1.0.tar.gz:
Publisher:
publish.yml on opratr/pqcprobe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pqcprobe-0.1.0.tar.gz -
Subject digest:
ac5739f63fcb7de031b1cf08b390599ff3b8fd8739bfc4464644451b944c2772 - Sigstore transparency entry: 2074682018
- Sigstore integration time:
-
Permalink:
opratr/pqcprobe@2f13a0dc1b3bf94d3ac688223b870253ddab8a01 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/opratr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f13a0dc1b3bf94d3ac688223b870253ddab8a01 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pqcprobe-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pqcprobe-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5b8c049952955bbfaf8d8ba7b280ea4657f2a45c926c5dde2ef459343c9f6a2
|
|
| MD5 |
366d5c6621adff753588fd73cdbdc132
|
|
| BLAKE2b-256 |
0bf01a139d87fa95ca63ee2032bbb332f3811122bc1f81b7e6e893e16476f83d
|
Provenance
The following attestation bundles were made for pqcprobe-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on opratr/pqcprobe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pqcprobe-0.1.0-py3-none-any.whl -
Subject digest:
e5b8c049952955bbfaf8d8ba7b280ea4657f2a45c926c5dde2ef459343c9f6a2 - Sigstore transparency entry: 2074682061
- Sigstore integration time:
-
Permalink:
opratr/pqcprobe@2f13a0dc1b3bf94d3ac688223b870253ddab8a01 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/opratr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f13a0dc1b3bf94d3ac688223b870253ddab8a01 -
Trigger Event:
release
-
Statement type: