Point the auditor at a Java source tree and it returns the code that has to change when RSA, ECDSA and (EC)DH are retired, ranked by how expensive each site is to migrate, together with an ordered migration plan and an engineer-time estimate for the work. It needs no build and no classpath: the scan is syntactic, runs on the source alone, and takes a few seconds for a mid-sized project.
The first screen of readiness-report.md for Eclipse Californium 3.14.0, one of
the four case studies. The full report continues with the
module ranking and the ranked hotspots, each with a file:line and the reason it
is expensive.
The tool is the instrument of a research project on a question current post-quantum-cryptography guidance leaves open: how much a migration will cost for a specific Java codebase, and where the expensive parts hide. Every score and time figure it produces today is a Phase 1 estimate from a pre-registered model, not a validated prediction; Research question and Status below say what has and has not been validated.
Try it in your browser, nothing to install.
Launch a Codespace and the dev container builds the auditor and checks out a
bundled case study. When it is ready, run ./scan.sh to scan the jjwt study, or
./scan.sh path/to/your/project to scan your own code. The same scan.sh works
on any local clone.
Run the auditor on any Java source tree in one command: no build, no classpath wrangling. Download the self-contained CLI jar from Maven Central and point it at a directory:
# Download the executable auditor (fat jar) from Maven Central
curl -L -o pqc-readiness-auditor.jar \
https://repo1.maven.org/maven2/io/github/arpan0995/pqc-readiness-auditor/1.4.0/pqc-readiness-auditor-1.4.0-all.jar
# Scan a codebase; writes JSON + Markdown + SARIF reports to ./audit-out
java -jar pqc-readiness-auditor.jar /path/to/java/project --out audit-out --name my-projectOutput: audit-out/readiness-report.json (machine-readable),
audit-out/readiness-report.md (ranked hotspots with file:line and why each is
expensive), and audit-out/readiness-report.sarif (SARIF 2.1.0).
The Markdown report opens with a migration plan at a glance: the ordered
steps a PQC migration of the scanned codebase needs (provider setup, key-type
decoupling, ML-KEM/ML-DSA call-site moves, keystore re-issue, testing and
rollout), which modules each step touches, and an engineer-time range per step
split into change work and testing. The time figures come from a declared
planning-time model layered on the difficulty score (1 point ≈ 1.5–3
engineer-hours of change work, testing ≈ 50–100% of change); they are planning
heuristics, not validated predictions (see
docs/research/03-difficulty-scoring-model.md §8.1). The same plan is embedded
in the JSON report as the additive migrationPlan node, assumptions included.
Build-output directories (target, build, out, bin) are pruned by default
so a scan of a built tree matches a scan of a clean checkout; override with
--exclude <dirs>, or pass an empty value to scan everything. Findings under
conventional test source roots are labelled and ranked separately, because test
code exercises vulnerable algorithms deliberately; add --skip-tests to leave
them out of the scan entirely.
Surface findings in GitHub code scanning: upload the SARIF report from any workflow and findings appear as pull-request annotations and Security-tab alerts. Scan the repository root so alert paths resolve:
- run: |
curl -L -o pqc-readiness-auditor.jar \
https://repo1.maven.org/maven2/io/github/arpan0995/pqc-readiness-auditor/1.4.0/pqc-readiness-auditor-1.4.0-all.jar
java -jar pqc-readiness-auditor.jar . --out audit-out
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: audit-out/readiness-report.sarifThe same steps are packaged as a GitHub Action,
Arpan0995/pqc-readiness-action,
which downloads the auditor, scans the checkout, writes a summary to the job page
and uploads the SARIF report:
- uses: Arpan0995/pqc-readiness-action@v1Use the libraries in a Maven build:
<!-- Static readiness auditor (scanner + pre-registered scoring model) -->
<dependency>
<groupId>io.github.arpan0995</groupId>
<artifactId>pqc-readiness-auditor</artifactId>
<version>1.4.0</version>
</dependency>
<!-- Runtime crypto-agility layer (classical / hybrid / PQC-only) -->
<dependency>
<groupId>io.github.arpan0995</groupId>
<artifactId>pqc-readiness-agility</artifactId>
<version>1.4.0</version>
</dependency>Run the audit from a Maven build, with no pom changes required:
mvn io.github.arpan0995:pqc-readiness-maven-plugin:1.4.0:auditAggregates once at the multi-module root and writes all three reports to
target/pqc-readiness/. Build output directories (target/) are excluded from
the scan, so generated sources are never audited. Configurable via
-Dpqc.readiness.sourceRoot, -Dpqc.readiness.out, -Dpqc.readiness.name,
-Dpqc.readiness.excludedDirectories, -Dpqc.readiness.skipTests, and
-Dpqc.readiness.skip.
NIST finalized ML-KEM (FIPS 203), ML-DSA (FIPS 204), and SLH-DSA (FIPS 205) in August 2024; NIST IR 8547 deprecates quantum-vulnerable algorithms (RSA, ECDSA, (EC)DH, EdDSA, …) after 2030 and disallows them after 2035. Organizations know the destination. What no existing tool provides is a validated estimate of migration effort: existing scanners (CBOM inventories, misuse detectors) stop at listing crypto usage. Meanwhile almost all PQC tooling research targets C/C++/Rust. The enterprise JVM ecosystem is nearly unstudied, even though JDK-native PQC only began arriving in JDK 24+ and most production fleets sit on JDK 8–21 for years to come.
Can static code patterns in a Java codebase predict the actual effort required to migrate it to post-quantum cryptography?
Hypothesis (H1): a difficulty score built from (a) crypto API usage patterns and (b) structural fragility indicators (hardcoded key/signature buffer sizes, fixed-width serialization, protocol pinning, concrete key-type coupling) predicts measured migration effort better than naively counting crypto call sites. The validated scoring methodology is the research contribution; the tool is the instrument. A negative result is a result.
Current phase: the project runs in two phases (see doc 03 §8). Phase 1 (now) runs the auditor against real public codebases and produces a score-derived effort estimate, a transparent heuristic, not yet a validated prediction. Phase 2 (deferred) performs the actual correlation study against measured migration effort (doc 04/05, unchanged, just not started). Read every score/tier in this repo today as Phase 1 output.
- Detect (
auditor): JavaParser-based scan for quantum-vulnerable JCA/JCE usage, validated by precision/recall against hand-labeled ground truth. - Score (
auditor): pre-registered difficulty model (weights frozen before effort data is collected) per module/file. - Validate (Phase 2, deferred, not currently active): migrate real open-source codebases to hybrid crypto and measure actual effort (files touched, LOC, breakages, time), plus a second track mining projects that already migrated for real (e.g., Apache Mina SSHD's ML-KEM hybrid adoption). Correlate predicted score vs. measured effort (Spearman ρ), against the naive-count baseline.
- Provide agility (
agility-provider): a policy-driven runtime layer over JCA/JCE for classical / hybrid / PQC-only modes with capability negotiation and audit logging, benchmarked (benchmarks, JMH) to answer the standard "abstraction layers are too slow" objection with data.
Phase 1 (now):
- Per-codebase readiness report: module scores, a qualitative effort
tier (
NONE/LOW/MEDIUM/HIGH/CRITICAL), and ranked hotspots with file:line and why each is expensive: an estimation aid, not a validated prediction (see doc 03 §8). - Benchmark tables: agility-layer overhead across modes, incl. JVM-specific
effects (GC/allocation pressure from multi-KB PQC artifacts). Done, see
benchmarks/results/RESULTS.md.
Phase 2 (deferred):
- Correlation figure: predicted difficulty vs. measured migration effort.
- Precision/recall table for the detector, against hand-labeled ground truth.
| Module | Role |
|---|---|
auditor |
Scanner + difficulty scoring |
agility-provider |
Runtime crypto-agility layer (BC-backed, JDK 21) |
benchmarks |
JMH harness for agility-layer overhead |
maven-plugin |
pqc-readiness-maven-plugin: runs the auditor from a Maven build |
case-studies |
Pinned target codebases + migration effort logs |
docs/PROJECT.md is the authoritative project overview and current-state
document (what's built, key results, deviations from the original plan, what remains).
Design decisions and methodology live in docs/research/:
- Background & motivation: threat model, verified standards status, the gap
- Detection rule catalog: what the auditor flags and why
- Difficulty scoring model: pre-registered score v0
- Case-study plan: selection criteria, migration & effort-logging protocol
- Validation & benchmark plan: statistics, interpretation grid, JMH matrix
- Agility provider design: policy, negotiation, hybrid composition
- JDK 21, Maven 3.9+
- Crypto primitives via
org.bouncycastle:bcprov-jdk18on(ML-KEM/ML-DSA/SLH-DSA are bundled in the main provider jar, so there is no separatebcpqartifact)
mvn clean install -Dgpg.skip=true
Release builds sign every artifact with GPG during the verify phase, so a
local build fails at that step unless a signing key is configured. The flag
skips signing; nothing else changes.
The published -all jar (see Install / Run) is the easiest way
to run the auditor. To run it from a local build instead:
mvn -pl auditor -am package
java -jar auditor/target/pqc-readiness-auditor-1.4.0-all.jar <source-root> --out audit-out --name <label>
Produces audit-out/readiness-report.json (machine-readable, feeds the analysis
harness) and audit-out/readiness-report.md (ranked hotspots with reasons).
Correlation harness (pure-stdlib Python) lives in analysis/:
python3 analysis/correlate.py --selftest # validate the statistics
python3 analysis/correlate.py --report <report.json> --effort <effort.csv>
Full JMH campaign (Apple M2, JDK 21.0.11, BC 1.84, @Fork(2)/@Warmup(5)/
@Measurement(5), -prof gc) across classical / hybrid / PQC postures. Full tables,
methodology, and raw data in benchmarks/results/RESULTS.md.
Headlines:
- Negotiation overhead is negligible. Capability negotiation costs ~6–9 ns and 24 B/op, 4–5 orders of magnitude below the crypto it selects. The "abstraction layer is too slow" objection does not survive the data.
- PQC is not uniformly slower. ML-KEM encapsulate/decapsulate are faster than X25519 (0.3× / 0.7×); the cost moves to keygen. ML-DSA signing is the expensive operation (7.8× ECDSA; dual-sign 5.4×), while verification stays modest (1.5–1.7×).
- The JVM-specific cost is allocation. Hybrid/PQC operations allocate 4–13× more per op (hybrid KE keygen 47 KB/op vs 3.7 KB classical; ML-DSA sign 342 KB/op vs 53 KB), i.e. sustained GC pressure the C/Rust-heavy PQC literature does not surface.
| Operation | Classical | Hybrid | PQC-only |
|---|---|---|---|
| KEM keygen | 43.6 µs | 102.4 µs (2.3×) | 55.6 µs (1.3×) |
| KEM encapsulate | 108.0 µs | 138.2 µs (1.3×) | 36.1 µs (0.3×) |
| Signature sign | 65.6 µs | 355.9 µs (5.4×) | 513.7 µs (7.8×) |
| Signature verify | 72.3 µs | 171.7 µs (2.4×) | 119.9 µs (1.7×) |
Read the ratios, not the absolute µs: these are microbenchmarks on one machine. Reproduce on target hardware before quoting figures. Must be run from the module classpath, not the shaded jar (BC's ML-KEM is a multi-release jar the shade plugin flattens, see RESULTS.md).
Phase 1 (estimation, current focus):
- Multi-module scaffolding, build-verified on JDK 21 (JMH runs end-to-end)
- Research phase: standards verified (2026-07-08), detection catalog, pre-registered scoring model v0
-
auditordetection waves 1–3: JCA entry points (Cipher, KeyPairGenerator, KeyFactory, KeyAgreement, Signature) and structural fragility indicators F1 (fixed buffers), F3 (TLS/protocol pinning), F4 (type coupling), F6 (persisted key material) - Scoring engine (score v0), qualitative effort tier, JSON + Markdown reports, CLI, all build-verified end-to-end
-
agility-provider: negotiation core (suites, policy, capability negotiation, audit log) + BC-backed hybrid primitives (KEM combiner, dual signature), 20 tests - Auditor detection: intra- and cross-file constant propagation
(
getInstance(Type.FIELD)resolved without a classpath) - JMH benchmark campaign run across classical/hybrid/PQC, with results and
interpretation in
benchmarks/results/RESULTS.md(negotiation overhead ~6–9 ns; PQC allocation 4–13× classical) - Auditor run against 4 real public codebases: Phase 1 estimation reports +
synthesis in
case-studies/ - Remaining detection (optional): F2 (fixed-width persistence), F8 (third-party API boundary), JOSE/JWT surface, enum/registry & dynamic algorithm selection
Phase 2 (validation, deferred, not started):
- Case-study migrations (real or mined) and effort logging (
docs/research/04) - Correlation analysis: score vs. measured effort (
docs/research/05).analysis/correlate.pyexists and is validated against synthetic data only; real case-study data is what's missing - Precision/recall against hand-labeled ground truth
Research prototype. Not production crypto software. The auditor's detection is syntactic (no classpath required); its accuracy is to be measured, not assumed. Every score/tier currently produced is a Phase 1 estimate, not a validated prediction.
PQC readiness of the Java ecosystem runs the auditor over the latest release of 27 widely used open-source Java projects and ranks them by estimated migration effort, from Apache MINA SSHD and jjwt at the top to projects that come back clean. It is a Phase 1 estimate meant to be corrected: if a ranking looks wrong for a project you know, say so in the scan-results discussion.
Questions, scan results from real codebases, bug reports and pull requests are all welcome. Open a Discussion to ask about the approach or to share what the auditor found on your code, raise an Issue for bugs and detection gaps, or send a pull request. CONTRIBUTING.md has the details and a list of where help is most useful right now.
If you find this project interesting or useful, please give it a star. Stars are the quickest way to show that the work is worth continuing, and they help others working on Java PQC migration find it.
If you use this work, please cite it via CITATION.cff (also
surfaced by GitHub's "Cite this repository" button).
