Skip to main content

Post-quantum cryptographic discovery, CycloneDX 1.6 CBOM generation, and quantum readiness scoring

Project description

QuantumShield

Post-quantum cryptographic discovery, CBOM generation, and quantum readiness scoring.

QuantumShield scans a codebase or filesystem for cryptographic usage, parses certificates and key material, and answers the first question of any PQC migration: what quantum-vulnerable cryptography do we actually have, and where?

It emits a CycloneDX 1.6 Cryptographic Bill of Materials (CBOM) and a self-contained HTML report with a 0-100 quantum readiness score.

Quick start

pip install quantumshield-pqc          # add [certs], [js], [web], or [all] extras
quantumshield scan /path/to/repo -o results/

The PyPI distribution is quantumshield-pqc (the quantumshield name was already taken); the command and import quantumshield are unchanged. Optional extras: certs (X.509 parsing), js (JavaScript AST detection via esprima), web (the demo web UI). The core is stdlib-only.

From a source checkout for development:

pip install -e ".[dev]"

Outputs:

File What it is
results/cbom.cdx.json CycloneDX 1.6 CBOM — cryptographic-asset components with evidence (file + line), OIDs, NIST quantum security levels
results/report.html Self-contained report: readiness score, exposure spectrum, per-finding remediation guidance

Exit code is 1 when CRITICAL findings exist, so it drops straight into CI as a quantum-exposure gate:

- name: PQC exposure gate
  run: quantumshield scan . --json-only

Tune the threshold with --fail-on {critical,high,medium,low,any,never}.

Living with a real repository

A first scan of an established codebase finds years of accumulated crypto, most of which won't be fixed this sprint. Three ways to quieten it, in increasing precision:

Baseline — record what exists today, then gate only on what's new. This is what lets a team adopt the CI gate without fixing the backlog first:

quantumshield scan . --write-baseline .quantumshield-baseline.json   # once, commit it
quantumshield scan . --baseline .quantumshield-baseline.json         # in CI

Fingerprints ignore line numbers, so a baselined finding stays baselined when unrelated edits push it up or down the file.

.quantumshieldignore — gitignore-style path globs at the scan root, for directories that should never be scanned (! negation is not supported):

thirdparty/
**/generated/**
*.min.js

Inline suppressions — per-line, reviewable in code review, and they travel with the code. Any comment syntax works:

h = hashlib.sha1(data)  # quantumshield: ignore[SHA-1] legacy vendor feed, JIRA-123

ignore alone suppresses every finding on the line; ignore[MD5,SHA-1] limits it to named algorithms. Everything suppressed is counted in the run summary, so nothing disappears silently.

SARIF for code-scanning dashboards

quantumshield scan . --sarif        # writes results.sarif alongside the CBOM

SARIF 2.1.0, so findings land in GitHub code scanning (or any SARIF-consuming dashboard) with file, line, severity and a stable fingerprint per alert:

- run: quantumshield scan . --sarif --fail-on never
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: quantumshield-out/results.sarif

SAFE findings are deliberately not emitted as SARIF alerts — they're positive detections (you already use AES-256 / ML-KEM here), and filing them as alerts would raise bugs against correct code. They remain in the CBOM and HTML report.

Try it on the included fixture:

quantumshield scan examples/vulnerable-demo -o demo-results/

Live TLS probing

probe performs a real TLS handshake against one or more host:port targets and reports the negotiated protocol, cipher suite, and — for TLS 1.3 targets — the negotiated key-exchange group, including hybrid PQC groups (e.g. X25519MLKEM768). Findings flow through the same CBOM/scoring/report pipeline as scan.

quantumshield probe example.com:443 legacy-host:443 -o probe-results/
QuantumShield v0.4.0 — probing 2 target(s) ...
  Targets probed: 2 | reachable: 2
  Quantum readiness: 92/100 (grade A)
    HIGH     1
    example.com:443       TLSv1.3 | TLS_AES_256_GCM_SHA384 | group=X25519MLKEM768
    legacy-host:443       TLSv1.1 | ECDHE-RSA-AES256-SHA | group=unknown (TLS<1.3)

Demo web UI

A browser front-end for demonstrating a scan: enter a repo path, get the live dashboard (score, exposure spectrum, per-finding evidence). Needs the [web] extra.

pip install -e ".[web]"
quantumshield serve            # http://127.0.0.1:8000

Routes: / (scan form + embedded report), /report?path=… (the HTML report), /api/scan?path=… (raw CycloneDX 1.6 CBOM as JSON), /healthz.

What it detects

  • Source code (Python, JS/TS, Java, Go, C/C++, Rust, C#, Ruby, PHP, and more): RSA, ECC/ECDSA/ECDH, DH, DSA, MD5, SHA-1, DES/3DES, RC4, Blowfish, AES (by key size), SHA-2/SHA-3 family, ChaCha20 — plus positive detection of PQC adoption (ML-KEM/Kyber, ML-DSA/Dilithium, SLH-DSA/SPHINCS+)
  • Python & JS via AST (not regex): Python files are analysed with the stdlib ast module, and JavaScript (.js/.mjs/.cjs) with esprima when the optional [js] extra is installed. Detection fires only on real call sites — resolved through the file's own import aliases — never on keywords in comments or strings, and structured detail is read straight from the arguments: RSA key size (generate_private_key(key_size=3072), generateKeyPairSync('rsa', {modulusLength: 3072})), EC curve names, and WebCrypto AES key length. Files that fail to parse fall back to regex.
  • Config files: weak TLS protocol versions and cipher suites in nginx, Apache, sshd, OpenSSL configs
  • Certificates & keys: parses X.509 (PEM/DER) for public key algorithm, key size, signature algorithm, and expiry; flags quantum-vulnerable private key material on disk (header detection only — private keys are never parsed)
  • Live TLS handshakes (probe): negotiated protocol version, cipher suite, and — for TLS 1.3 — key-exchange group, including hybrid PQC groups (X25519MLKEM768, IANA group id 0x11ec/4588)

Severity model

Severity Meaning Examples
CRITICAL Shor-breakable on a CRQC; harvest-now-decrypt-later exposure RSA, ECDSA, ECDH, DH, DSA, vulnerable certs
HIGH Classically broken or deprecated today MD5, SHA-1, DES, 3DES, RC4, TLS <= 1.1
MEDIUM Grover-reduced security margin AES-128, SHA-224
LOW Acceptable but monitor SHA-256
SAFE Quantum-ready AES-256, SHA-384/512, SHA-3, ML-KEM, ML-DSA, SLH-DSA

Readiness score: starts at 100; each vulnerable asset deducts a severity weight plus a capped per-occurrence penalty, so widespread usage scores worse than a stray import. Grades: A >= 90, B >= 75, C >= 55, D >= 35, else F.

Development

pip install -e ".[dev]"
pytest          # 166 tests

Architecture, conventions, and roadmap live in CLAUDE.md — the repo is set up for AI-assisted development with Claude Code.

Roadmap

  • Engine 2 — network: live TLS handshake probing (protocol, cipher suite, key-exchange group, hybrid PQC detection e.g. X25519MLKEM768)
  • AST-based detection (Python + JavaScript) to cut false positives and capture key sizes
  • Mosca-inequality migration urgency modelling per asset
  • Dependency/lockfile crypto analysis
  • Multi-repo tracking and CBOM diffing over time

Known limitations

  • Python and JavaScript (.js/.mjs/.cjs, with the [js] extra) are analysed by AST and don't suffer comment/string false positives. Other source — and TypeScript/JSX, which esprima doesn't parse — is matched by regex, which can miss dynamically constructed algorithm names and may flag keywords in comments or strings; review evidence lines in the report.
  • AST detects recognised crypto API calls, not hand-rolled implementations: a bespoke RC4 written out as array operations won't be flagged (regex would catch the keyword, at the cost of false positives). Use a real cipher call (crypto.createCipheriv('rc4', …)) and it's caught.
  • AST detection (Python) reads key sizes from call arguments only when they're statically visible. An AES key loaded at runtime (e.g. from a KMS/env) can't be sized, so AESGCM(runtime_key) is recorded as AES usage but without a key-size severity rather than guessing one. Determinable forms (AESGCM(AESGCM.generate_key(bit_length=256)), AES.new(os.urandom(32), …)) are sized correctly.
  • Bare-acronym collisions (e.g. DES as a partner/service name in a comment): fixed for Python by AST detection, which only inspects real call sites. The same collision can still occur in non-Python source that goes through regex — review evidence lines for any DES/3DES finding there before treating it as real cipher usage.
  • PKCS#8 (BEGIN PRIVATE KEY) headers don't reveal the algorithm without parsing, so unlabelled private keys aren't attributed (the matching certificate usually is).
  • probe's key-exchange group detection only works against TLS 1.3 servers. For TLS <= 1.2, the negotiated curve/group is carried in the ServerKeyExchange handshake message rather than a ServerHello extension, which probe doesn't parse — those targets still get protocol/cipher findings, just no group finding.
  • probe crafts its own ClientHello and never completes real key exchange (the connection is torn down right after reading the ServerHello), so it can't detect PQC hybrid groups behind a load balancer or WAF that decides differently for a "real" client than for the bare handshake probe sends.

License

MIT

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

quantumshield_pqc-0.4.0.tar.gz (56.0 kB view details)

Uploaded Source

Built Distribution

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

quantumshield_pqc-0.4.0-py3-none-any.whl (44.5 kB view details)

Uploaded Python 3

File details

Details for the file quantumshield_pqc-0.4.0.tar.gz.

File metadata

  • Download URL: quantumshield_pqc-0.4.0.tar.gz
  • Upload date:
  • Size: 56.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for quantumshield_pqc-0.4.0.tar.gz
Algorithm Hash digest
SHA256 24317de8bec12bd06ddda1c026761286361aeaa0116fad2c557d43fad5d7bd76
MD5 79ef50f930fbc5944ec26515b23af672
BLAKE2b-256 41f374007ce3753337fdf86d56bf4eb522915ed343099b059f332e7d57238beb

See more details on using hashes here.

Provenance

The following attestation bundles were made for quantumshield_pqc-0.4.0.tar.gz:

Publisher: publish.yml on 4p3Ir0n/quantumshield

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

File hashes

Hashes for quantumshield_pqc-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d9d74c70ef54fa3aee87c8a9daf0fb24c20e2b9ca8cfafebe3840d0ecfd21bdc
MD5 06d8b6ad794ce0549f6844fcf500c16a
BLAKE2b-256 2a2fd872c3ac9b06d7d00c752e9eac84182cc89e36835e3e614f4fbe443dd59a

See more details on using hashes here.

Provenance

The following attestation bundles were made for quantumshield_pqc-0.4.0-py3-none-any.whl:

Publisher: publish.yml on 4p3Ir0n/quantumshield

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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