Metadata-Version: 2.4
Name: pqcpilot
Version: 0.1.0
Summary: Find quantum-vulnerable cryptography, generate a CycloneDX CBOM, and plan the move to post-quantum algorithms.
License-Expression: Apache-2.0
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Provides-Extra: certs
Requires-Dist: cryptography>=42; extra == "certs"
Provides-Extra: pq
Requires-Dist: cryptography>=48; extra == "pq"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: cryptography>=48; extra == "dev"

# pqcpilot

Find quantum-vulnerable cryptography in your code, produce a CycloneDX Cryptographic Bill of
Materials (CBOM), and get a readiness report mapped to the post-quantum deadlines that apply to you.

RSA, elliptic-curve cryptography (ECDSA, ECDH, EdDSA, X25519) and Diffie-Hellman can all be broken
by a large quantum computer. Regulators now set dates to replace them:

- **India**: the DST Quantum-Safe Roadmap (May 2026) expects critical sectors, including banking and
  finance, to complete a cryptographic inventory by December 2027 and fully adopt PQC by December 2029,
  with vendor CBOMs from FY 2027-28.
- **United States**: NIST IR 8547 (draft) deprecates 112-bit algorithms such as RSA-2048 in 2030 and
  disallows all quantum-vulnerable public-key algorithms, including P-256, in 2035. CNSA 2.0 sets
  earlier dates for national security systems.

## Install

```bash
pip install -e .            # no required dependencies
pip install -e ".[certs]"   # adds certificate and key parsing via pyca/cryptography
```

## Use

```bash
pqcpilot scan path/to/repo                                   # terminal summary
pqcpilot scan . --format cbom -o cbom.json                   # CycloneDX 1.6 CBOM
pqcpilot scan . --format markdown --profile india-cii -o pqc-report.md
pqcpilot scan . --skip-tests --fail-on critical              # CI gate
```

Profiles: `india-cii` (default), `india-enterprise`, `nist`, `cnsa2`.

## Fix

```bash
pqcpilot fix .                                   # show the diff of safe fixes, change nothing
pqcpilot fix . --apply --pr-body pr.md           # apply them and write a pull request body
pqcpilot init-ci                                 # add a GitHub Actions workflow
```

`fix` does three things:

1. **Safe in-place fixes.** Raises RSA and DH keys below 3072 bits, and replaces P-192 / P-224 and
   python-ecdsa's default NIST192p with P-256. These change parameters, not protocols, so the other
   end keeps working. Sizes pinned by a constant are left for the engineer.
2. **A hybrid helper module.** Drops `pqc_hybrid.py` into the project's package: X25519 + ML-KEM-768 key
   encapsulation and Ed25519 + ML-DSA-65 signatures, built on `cryptography>=48`. Nothing is wired in
   automatically, because both peers of a protocol have to move together.
3. **A migration checklist.** The PR body lists every remaining finding with what to change, grouped and
   ordered by urgency, plus the regulatory timeline.

Findings in tests, fixtures and examples are never rewritten: a test may use a small key on purpose to
check that it is rejected. They are listed in the checklist (`--include-tests`).

The workflow from `init-ci` scans on every push, publishes the CBOM, fails on critical findings in
production code, and once a week opens a pull request with the fixes and checklist.

## Check live endpoints

```bash
pqcpilot tls www.example.com api.example.com:8443
pqcpilot tls --hosts-file endpoints.txt --format markdown -o tls-report.md
```

For each endpoint it sends a hand-built TLS 1.3 ClientHello that offers X25519MLKEM768 (the hybrid
key exchange browsers use) and reports which group the server chose, whether it supports the hybrid
at all, the TLS version, and the certificate's key. It works with any OpenSSL, because it does not
rely on the local library knowing about ML-KEM. No handshake is completed and nothing is sent after
the ServerHello. A server that is not quantum-safe is rated **high**, because recorded traffic can
be decrypted later.

## What it detects

| Source | Coverage |
| --- | --- |
| Python | pyca/cryptography, pycryptodome / pycryptodomex, python-rsa, python-ecdsa, paramiko, JWT algorithms (PyJWT, python-jose) |
| JavaScript / TypeScript | Node `crypto`, WebCrypto `subtle`, jsonwebtoken / jose `alg` values, node-forge, elliptic, tweetnacl |
| Java / Kotlin | JCA/JCE `getInstance` (KeyPairGenerator, Cipher, Signature, KeyAgreement, KeyFactory) with the key size or curve from `initialize(...)`, BouncyCastle lightweight API, Nimbus / jjwt / java-jwt algorithms |
| Go | `crypto/rsa`, `crypto/ecdsa`, `crypto/ecdh`, `crypto/ed25519`, `crypto/dsa`, `x/crypto/curve25519`, `x509` key parsing, golang-jwt, and `tls.Config.CurvePreferences` lists that disable Go's default X25519MLKEM768 |
| Files | PEM certificates and keys (parsed for real key size and curve), OpenSSH public keys |

Severity:

- **Critical**: weak even today (RSA under 2048 bits, DSA, P-192 / P-224, small DH groups).
- **High**: key exchange and encryption, exposed to harvest-now-decrypt-later.
- **Medium**: signatures, which break once a large quantum computer exists.
- **Low**: the classical half of a hybrid post-quantum scheme (the file also uses ML-KEM).

Findings in tests, fixtures and examples are tagged so reports can separate them from production code.

## Limits

It reads source and files only. It does not see cryptography inside compiled dependencies, HSMs,
cloud KMS or live TLS endpoints yet. Keys loaded from files at runtime (`load_pem_private_key`) are
not reported because their algorithm is unknown until runtime.

The live endpoint check does not echo cookies. A server that answers with a stateless HelloRetryRequest
asking only for a cookie is reported as not assessed, not rated.

Findings are tagged as tests, fixtures or examples from the path alone: a directory named `test`,
`tests`, `spec`, `specs`, `__tests__`, `__mocks__`, `fixtures`, `testdata`, `tap`, `test-d`, `examples`,
`cookbook` or `demo`, or a file named `test_*`, `*.test.*`, `*.spec.*` or `*.test-d.*`. A production
directory with one of those names is tagged the same way, and `--skip-tests` drops its findings.

Known misses in the Java, Kotlin and Go scanners:

- Go dot-imports of a JWT library (`import . "github.com/golang-jwt/jwt/v5"`): the signing methods are
  unqualified, so they are not matched. Aliased and unaliased imports are.
- Kotlin scope functions other than `apply` and `run`, such as `with(gen) { initialize(n) }`: the
  generator's size stays unknown.
- BouncyCastle's `ECMQV` key agreement is not mapped.
- `ssh.ParsePrivateKey` and `ssh.ParseRawPrivateKey` are not reported, because the key type is not visible
  at the call. `ssh.NewSignerFromKey` is RSA only for a key declared as an RSA key in the same function.

## Development

```bash
pip install -e ".[dev]"
pytest
```
