One CBOM. Every jurisdiction's verdict. And where they contradict.
Germany and France recommend hybrid post-quantum key exchange. Australia recommends against it. The NSA wants ML-KEM-1024, not the -768 the others accept. If you ship into more than one of those markets, those are not independent checkboxes — and no tool will show you the collision.
cbomctl takes a CBOM from any generator, runs it against several national PQC
policies at once, and reports the matrix and the conflicts.
⚠️ Pre-release. All seven policy packs are read from their primary sources — every rule cites the section or page it came from. The machinery for unverified rules stays in place for the next pack that has not been checked: any verdict derived from one carries a visible banner naming the packs involved. Read the sources yourself before acting on a verdict — they are listed in docs/policy-sources.md. Not compliance advice.
cbomctl verdict app-cbom.json --jurisdictions bsi-de,anssi-fr,asd-au,cnsa-2.0ASSET PURPOSE bsi-de anssi-fr asd-au cnsa-2.0
───────────────────────────────────────────────────────────────────────
ECDH key-agreement WARN WARN WARN FAIL ⚠ c1
└ bsi-de · disallowed 2031-12-31
└ anssi-fr · complete 2030-12-31
└ asd-au · disallowed 2030-12-31 · complete 2030-12-31
└ cnsa-2.0 · disallowed 2030-12-31 · exclusive_use 2031-12-31
└ src/payments/legacy.go:12
X25519MLKEM768 key-agreement PASS PASS WARN FAIL ⚠ c2,c3
└ asd-au · deprecated 2030-12-31
└ src/payments/tls.go:88
ML-DSA-65 signature WARN WARN WARN FAIL ⚠ c4,c5
└ asd-au · deprecated 2030-12-31
└ src/payments/sign.go:7
RSA-2048 ambiguous INDET INDET INDET INDET
└ unresolved: purpose-ambiguous (primitive:pke)
└ as key transport → critical · as signature → medium
└ Declare the purpose in cbomctl.yaml, or regenerate the CBOM with a generator that records
cryptoFunctions.
└ src/payments/keys.go:41
CONFLICTS (5)
c1 [construction] ECDH
anssi-fr, bsi-de recommend a hybrid construction for key-agreement; asd-au recommend
against it; cnsa-2.0 does not permit one outside named interoperability exceptions.
cnsa-2.0 does not permit a hybrid construction outside named interoperability exceptions,
while anssi-fr, bsi-de recommend one. **No single configuration satisfies all selected
jurisdictions.** You will need different builds, or to drop a jurisdiction from scope.
asd-au would permit a hybrid but recommends against it, so even dropping cnsa-2.0 leaves a
documented cost. This is a business decision, not a technical one.
⚖ This conflict depends on a contested encoding. Under the alternative reading (cnsa-2.0
silent), a hybrid construction would satisfy all selected jurisdictions, at a documented
cost. The argument and the evidence for the encoding used are in the `cnsa-2.0` rule's
interpretation note (`cbomctl policies show cnsa-2.0`).
WARN rather than FAIL for ASD is deliberate: the ISM recommends against
hybrids, it does not prohibit them. Reporting a guideline as a mandate is the
most common error in this space, so every rule carries a binding field —
statute, executive_order, agency_requirement, certification_requirement,
or guideline_recommendation — and the verdict follows from it.
INDET is the tool declining to guess. That CBOM records primitive: pke for
the RSA key, which cannot distinguish key transport from signature — and the
answer moves the finding between critical and medium. Rather than pick, it says
what is missing and what would resolve it.
Most of what a PQC readiness tool does, other people already do — several of them well. Use them.
| tool | what it does |
|---|---|
| CBOMkit, cbomscanner, KeyLens, pqc-scanner | generate CBOMs from source |
| open-quantum-secure | scans source + live TLS/SSH, generates CBOM, quantum readiness score, --data-lifetime-years HNDL weighting, and seven compliance frameworks including BSI, ASD ISM and ANSSI |
| sbom-tools | CycloneDX/SPDX ingestion, semantic CBOM diff, quality scoring, CNSA 2.0 and NIST IR 8547 validation, SARIF/OSCAL |
| IBM Quantum Safe Migration Orchestrator, SandboxAQ AQtive Guard, O3 Security | commercial discovery, risk prioritization and migration planning |
cbomctl overlaps all of them, and only two things are actually its own:
- Conflict as a computed output.
open-quantum-secureruns multiple frameworks at once and its README documents cross-framework divergence directly — this is not a gap in the market, and anyone claiming otherwise has not read it. What it emits is one report per framework, concatenated; you find the disagreement by comparing them yourself.cbomctlemits the matrix, a conflict object naming the disagreeing authorities and the reason, and a computed satisfies-all target where one exists. - Evaluating a CBOM you did not generate. The tools above evaluate their
own scan. If your CBOM came from a vendor, a procurement process, or a
different generator,
cbomctlwill read it.
If you want a scanner, use open-quantum-secure. If you want diff and
validation, use sbom-tools. If you have a CBOM and need to know which
jurisdiction's answer to believe, that is this tool.
- It does not scan source code or probe endpoints. Bring a CBOM.
- It does not guess. In CBOMkit's published Keycloak CBOM, 8 of 22 algorithm components carry no usable purpose signal — more than a third. Those are reported as unresolved, with the range they would span if guessed.
- It does not give compliance advice. It reports what a rule set says, with
the primary source and verification date attached. All 40 rules shipped today
are verified against that source; the machinery for rules that are not stays
in place — such a rule ships
status: needs_verification, and every verdict derived from it carries a banner. - It does not treat guidance as law. See
binding, above. - It does not know when a CRQC arrives. 2035 is a planning assumption
matching the NIST/NSM-10 horizon, not a prediction.
--crqc-yearchanges it; whatever you choose is stamped into the output. - It does not diff SBOMs, verify signatures, or scan binaries.
cbomctl prioritize ranks findings by Mosca's inequality using data lifetimes
you supply, weighting harvest-now-decrypt-later exposure highest.
cbomctl plan emits an ordered migration plan. Both are useful; neither is
novel — see Related tools.
The rule sets live in policy-packs/ as a standalone,
semantically versioned artifact with its own schema and changelog, so another
tool can consume them without cbomctl. Every rule carries a primary-source
URL, a last_verified date, a binding classification, and a hybrid stance.
| pack | state |
|---|---|
bsi-de |
✅ TR-02102-1 v2026-01 §2.1, §5.3.4 |
anssi-fr |
✅ 5/6 — ANSSI 2023 follow-up §1.1, §1.2, §2, §3.2, §4 |
asd-au |
✅ ISM Guidelines for Cryptography, 2026-09-03 |
eu-roadmap |
✅ Coordinated Implementation Roadmap Part 1 v1.1 |
us-eo14412 |
✅ 91 FR 38483 §4(b), §5(c) |
nist-ir8547 |
✅ as a draft — IR 8547 ipd Tables 2 and 4 |
cnsa-2.0 |
✅ CNSA 2.0 FAQ v2.1 (Dec 2024), pp. 2, 6, 8, 19–20 |
pip install cbomctlOptional: pip install cbomctl[llm] adds a prose migration narrative that
cannot affect any verdict.
release.yml attaches a CycloneDX SBOM of cbomctl to every release it cuts,
as cbomctl-<version>.cdx.json, generated by
cyclonedx-py from a clean
install of the wheel being published. It is stamped as a build SBOM,
because which kind it is decides what it asserts: it names the versions this
project's dependency ranges resolved to, on one Python and one platform,
during that build. Nothing inspects the wheel's bytes, and no component
carries a hash — so it is not evidence of what your own install resolved.
It is an SBOM and not a CBOM, deliberately. No module in src/ imports
hashlib, hmac, secrets, ssl or cryptography: cbomctl reads
cryptographic inventories and performs none itself, so a CBOM of it would be
an empty components array with a CycloneDX header on it. That reads as
evidence and is not. scripts/check_stats.py derives the import count from
src/, so the sentence above fails the build rather than going quietly stale.
Design · Policy sources and open questions · Policy packs
Apache-2.0