You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
RFC: dependency readiness (the libraries you depend on, and which versions give you a PQC path)
#48
The source scan in this project is a floor, and two maintainers of scanned projects have said exactly where the floor is. On the Quarkus thread, Clément Escoffier confirmed that the real call sites live in Vert.x, Netty and Elytron, where the algorithm arrives through an API rather than a JCA literal (quarkusio/quarkus#56506). On the Californium side, cipher-suite and group support is decided at runtime through capability checks the scan cannot see (eclipse-californium/californium#2408). Registry and enum modelling (#5) is one answer to that. This RFC is about the other half of the problem, which no amount of source analysis fixes: the report knows nothing about the libraries a codebase depends on.
Two facts from our own ranking show what that costs.
Apache MINA SSHD sits at the top of the ecosystem scan at 337 findings and CRITICAL. It has also shipped ML-KEM hybrid key exchanges since 2.15.0 (mlkem768x25519-sha256, mlkem768nistp256-sha256 and mlkem1024nistp384-sha384, when Bouncy Castle is on the classpath; GH-606). A team that depends on sshd 2.19.0 already has a post-quantum path for key exchange, and the report has no way to say so.
jjwt is second at 193 findings, and as far as I can find no released jjwt (0.13.0 is the latest) offers a post-quantum signature algorithm. A team that depends on it is blocked on upstream, or on switching libraries, whatever their own code looks like.
For a migration plan, both facts matter more than the count of RSAPublicKey casts. The auditor knows neither.
Proposal
Three parts. The first two are independent of each other; the third needs both.
1. Read the declared dependencies, without a build
ModuleResolver already walks the tree for pom.xml, build.gradle and build.gradle.kts to find module boundaries. A DependencyExtractor would read the same files syntactically: <dependency> coordinates in a POM, with ${property} versions resolved from <properties> and the parent chain in the same tree, and string coordinates (implementation("g:a:v"), implementation 'g:a:v') in Gradle scripts. Version catalogs (libs.versions.toml) would be a follow-up. The no-build path sees declared dependencies only, and the report says so.
The Maven plugin runs inside a build, so it can do better: AuditMojo would request dependency resolution and hand the auditor the resolved, transitive set. Same output either way, with a source: declared | resolved field so a reader knows which they are looking at.
2. A curated PQC support matrix
A JSON resource in the auditor, one entry per crypto-relevant library, carrying: the coordinates (with a pattern where a family shares a prefix, such as org.bouncycastle:bcprov-*), what the library provides (KEM, signature, TLS, SSH, JOSE, WebAuthn, PKI), and per capability the first released version with a standardised post-quantum path, what that path is, and a citation. Every row needs a citation; a row without one does not land.
A sketch of the shape, with the four rows I can cite today:
[
{ "coordinates": "org.bouncycastle:bcprov-*",
"provides": ["kem", "signature"],
"pqPath": { "since": "1.79",
"what": "ML-KEM, ML-DSA and SLH-DSA in the BC provider and lightweight API (FIPS 203/204/205)",
"cite": "https://github.com/bcgit/bc-java/blob/r1rv79/docs/releasenotes.html" } },
{ "coordinates": "org.apache.sshd:sshd-core",
"provides": ["ssh"],
"pqPath": { "since": "2.15.0",
"what": "ML-KEM hybrid key exchanges, with Bouncy Castle present",
"cite": "https://github.com/apache/mina-sshd/issues/606" } },
{ "coordinates": "io.jsonwebtoken:jjwt-*",
"provides": ["jose"],
"pqPath": null,
"note": "no post-quantum JWS algorithm in any release through 0.13.0" },
{ "coordinates": "jdk",
"provides": ["kem", "signature"],
"pqPath": { "since": "24",
"what": "ML-KEM (JEP 496) and ML-DSA (JEP 497) in the JDK's built-in providers",
"cite": "https://openjdk.org/jeps/496" } }
]
The jdk row is the odd one: the target release in the build (maven.compiler.release, or the toolchain) tells us whether JDK-native ML-KEM and ML-DSA are even reachable, and most fleets are on 8 to 21.
Each dependency in use then gets one of four states: ready (the version in use has a path), upgrade (a later version has one, and the report names it), none (no released version has one, as far as the matrix knows), unknown (not in the matrix). The matrix is the part I would most like help with, and I would rather have twenty rows that are right than two hundred that are guessed.
3. Report it, without touching the score
A new dependencyReadiness node in the JSON report, next to migrationPlan and built the same way: a derived, additive view that feeds nothing back into scores, tiers, B0 or the t0 time model. Score model v0 stays frozen as pre-registered, since that is what the Phase 2 validation rests on, and the dependency layer gets its own version (d0) so reports stay comparable across releases.
In the Markdown report: a "Dependency readiness" section listing each crypto-relevant dependency, the version in use, its state, and the upgrade that would change it. In the migration plan: a new first step, "Upgrade dependencies that already have a post-quantum path", listing the concrete upgrades, because an upgrade that unlocks a path is usually the cheapest step in the plan. Dependencies in the none state are called out as blocked on upstream. In SARIF: an informational result at the pom.xml line of each upgrade and none dependency, so it shows up in code scanning next to the call-site findings.
Open questions
Should an upgrade row carry an effort figure? A major-version bump is not free, but the auditor has no data for it. My instinct is to name the upgrade and not price it.
Transitive dependencies in the no-build path: leave them out and say so, or accept an optional --dependency-list file (the output of mvn dependency:list or gradle dependencies) as input? I lean towards the file, because it keeps the no-build promise and still gives a complete picture where a build exists.
Matrix scope: only libraries that implement or expose asymmetric crypto (providers, TLS stacks, SSH, JOSE, WebAuthn, PKI), or also the frameworks that wrap them? A framework row could say "the path depends on X"; the note on the Quarkus thread that SmallRye JWT is moving to Nimbus for ML-DSA is exactly the kind of fact such a row would carry.
Ship parts 1 and 3 in 1.5.0 behind a flag, with a small matrix, and grow the matrix by pull request. Rows with citations for libraries you know (Nimbus JOSE+JWT, Conscrypt, wolfSSL JNI, Netty and tcnative, Tink, Keycloak, WildFly Elytron) are the most useful thing anyone can add, and maintainers of the scanned projects are the people best placed to say whether a row about their library is right.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
The source scan in this project is a floor, and two maintainers of scanned projects have said exactly where the floor is. On the Quarkus thread, Clément Escoffier confirmed that the real call sites live in Vert.x, Netty and Elytron, where the algorithm arrives through an API rather than a JCA literal (quarkusio/quarkus#56506). On the Californium side, cipher-suite and group support is decided at runtime through capability checks the scan cannot see (eclipse-californium/californium#2408). Registry and enum modelling (#5) is one answer to that. This RFC is about the other half of the problem, which no amount of source analysis fixes: the report knows nothing about the libraries a codebase depends on.
Two facts from our own ranking show what that costs.
mlkem768x25519-sha256,mlkem768nistp256-sha256andmlkem1024nistp384-sha384, when Bouncy Castle is on the classpath; GH-606). A team that depends on sshd 2.19.0 already has a post-quantum path for key exchange, and the report has no way to say so.For a migration plan, both facts matter more than the count of
RSAPublicKeycasts. The auditor knows neither.Proposal
Three parts. The first two are independent of each other; the third needs both.
1. Read the declared dependencies, without a build
ModuleResolveralready walks the tree forpom.xml,build.gradleandbuild.gradle.ktsto find module boundaries. ADependencyExtractorwould read the same files syntactically:<dependency>coordinates in a POM, with${property}versions resolved from<properties>and the parent chain in the same tree, and string coordinates (implementation("g:a:v"),implementation 'g:a:v') in Gradle scripts. Version catalogs (libs.versions.toml) would be a follow-up. The no-build path sees declared dependencies only, and the report says so.The Maven plugin runs inside a build, so it can do better:
AuditMojowould request dependency resolution and hand the auditor the resolved, transitive set. Same output either way, with asource: declared | resolvedfield so a reader knows which they are looking at.2. A curated PQC support matrix
A JSON resource in the auditor, one entry per crypto-relevant library, carrying: the coordinates (with a pattern where a family shares a prefix, such as
org.bouncycastle:bcprov-*), what the library provides (KEM, signature, TLS, SSH, JOSE, WebAuthn, PKI), and per capability the first released version with a standardised post-quantum path, what that path is, and a citation. Every row needs a citation; a row without one does not land.A sketch of the shape, with the four rows I can cite today:
[ { "coordinates": "org.bouncycastle:bcprov-*", "provides": ["kem", "signature"], "pqPath": { "since": "1.79", "what": "ML-KEM, ML-DSA and SLH-DSA in the BC provider and lightweight API (FIPS 203/204/205)", "cite": "https://github.com/bcgit/bc-java/blob/r1rv79/docs/releasenotes.html" } }, { "coordinates": "org.apache.sshd:sshd-core", "provides": ["ssh"], "pqPath": { "since": "2.15.0", "what": "ML-KEM hybrid key exchanges, with Bouncy Castle present", "cite": "https://github.com/apache/mina-sshd/issues/606" } }, { "coordinates": "io.jsonwebtoken:jjwt-*", "provides": ["jose"], "pqPath": null, "note": "no post-quantum JWS algorithm in any release through 0.13.0" }, { "coordinates": "jdk", "provides": ["kem", "signature"], "pqPath": { "since": "24", "what": "ML-KEM (JEP 496) and ML-DSA (JEP 497) in the JDK's built-in providers", "cite": "https://openjdk.org/jeps/496" } } ]The
jdkrow is the odd one: the target release in the build (maven.compiler.release, or the toolchain) tells us whether JDK-native ML-KEM and ML-DSA are even reachable, and most fleets are on 8 to 21.Each dependency in use then gets one of four states: ready (the version in use has a path), upgrade (a later version has one, and the report names it), none (no released version has one, as far as the matrix knows), unknown (not in the matrix). The matrix is the part I would most like help with, and I would rather have twenty rows that are right than two hundred that are guessed.
3. Report it, without touching the score
A new
dependencyReadinessnode in the JSON report, next tomigrationPlanand built the same way: a derived, additive view that feeds nothing back into scores, tiers,B0or thet0time model. Score model v0 stays frozen as pre-registered, since that is what the Phase 2 validation rests on, and the dependency layer gets its own version (d0) so reports stay comparable across releases.In the Markdown report: a "Dependency readiness" section listing each crypto-relevant dependency, the version in use, its state, and the upgrade that would change it. In the migration plan: a new first step, "Upgrade dependencies that already have a post-quantum path", listing the concrete upgrades, because an upgrade that unlocks a path is usually the cheapest step in the plan. Dependencies in the none state are called out as blocked on upstream. In SARIF: an informational result at the
pom.xmlline of each upgrade and none dependency, so it shows up in code scanning next to the call-site findings.Open questions
--dependency-listfile (the output ofmvn dependency:listorgradle dependencies) as input? I lean towards the file, because it keeps the no-build promise and still gives a complete picture where a build exists.What I would do first
Ship parts 1 and 3 in 1.5.0 behind a flag, with a small matrix, and grow the matrix by pull request. Rows with citations for libraries you know (Nimbus JOSE+JWT, Conscrypt, wolfSSL JNI, Netty and tcnative, Tink, Keycloak, WildFly Elytron) are the most useful thing anyone can add, and maintainers of the scanned projects are the people best placed to say whether a row about their library is right.
If you found PQC Migration Readiness interesting, please leave us a star on our GitHub repository ⭐ https://github.com/Arpan0995/pqc-migration-readiness
All reactions