Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/verify-v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Verify crypto-agility v2
on:
pull_request:
push:
branches: [main, review/automation-first-02]
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-24.04
timeout-minutes: 25
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: '3.12'
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
with:
node-version: '24'
- name: Record exact tested commit
run: |
mkdir -p build/results
git rev-parse HEAD | tee build/results/commit.txt
test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha || github.sha }}"
- name: Install pinned validation dependencies
run: |
python -m pip install -r requirements.txt
npm ci --ignore-scripts
- name: Build pinned OpenSSL with hybrid TLS support
run: |
bash scripts/build-test-openssl.sh build/openssl > build/results/openssl-build.log 2>&1
echo "OPENSSL_BINARY=$PWD/build/openssl/apps/openssl" >> "$GITHUB_ENV"
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: quantakrypto/pqc-tools
ref: 6a81b5537eaedf61f259334b4e430e580c35942c
path: build/pqc-tools
persist-credentials: false
- name: Build pinned qScan integration dependency
working-directory: build/pqc-tools
run: |
npm ci --ignore-scripts
npm run build
- name: Unit, adversarial, real TLS, interoperability and performance tests
env:
REQUIRE_HYBRID_TLS: '1'
REQUIRE_QSCAN_INTEROP: '1'
QSCAN_ROOT: ${{ github.workspace }}/build/pqc-tools
run: |
set -o pipefail
python scripts/check-tests.py 2>&1 | tee build/results/tests.log
- name: Validate v2 example (expected findings)
run: |
python tools/verifier.py examples/crypto-agility-v2.json --now 2026-10-12T00:00:00Z > build/results/assessment.json || test "$?" -eq 1
python -c 'import json; d=json.load(open("build/results/assessment.json")); assert d["status"]=="needs-review"; assert any(x["code"]=="fallback-risk" for x in d["findings"])'
- name: Render IETF XML without network references
run: |
python -m tools.render_draft --check
xml2rfc --no-network --text draft/draft-acosta-crypto-agility-manifest-02.xml --out build/results/draft-acosta-crypto-agility-manifest-02.txt
cmp build/results/draft-acosta-crypto-agility-manifest-02.txt draft/draft-acosta-crypto-agility-manifest-02.txt
xml2rfc --no-network --html draft/draft-acosta-crypto-agility-manifest-02.xml --out build/results/draft-acosta-crypto-agility-manifest-02.html
node_modules/.bin/idnits --mode submission --offline --output json draft/draft-acosta-crypto-agility-manifest-02.xml > build/results/idnits.json
python -c 'import json; d=json.load(open("build/results/idnits.json")); assert d["result"]=="pass" and not any(d["nitsBySeverity"].values())'
- name: Retain exact-commit validation evidence
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: validation-${{ github.event.pull_request.head.sha || github.sha }}
path: build/results/
retention-days: 30
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__pycache__/
*.pyc
.venv/
build/
node_modules/
84 changes: 23 additions & 61 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,72 +1,34 @@
# Contributing

Thanks for taking the time to look at the crypto-agility manifest. This is an early
individual proposal, so the most valuable contributions right now are review and
argument, not just code.
This is an individual proposal, not an IETF standard. Preserve version 1 while
reviewing version 2. The authoritative candidate is
`draft/draft-acosta-crypto-agility-manifest-02.xml`; its embedded normative schema
must equal `schema/crypto-agility-v2.schema.json`. Update both in the same PR.
Generate the Markdown rendering with `python -m tools.render_draft`.

## Where to start
Keep the core to discovery and evidence references. Reuse existing cryptographic
protocols. Distinguish declarations, authenticated assertions and independently
verified runtime behavior. Cite primary sources and separate normative dependencies
from background. Do not use em dashes or claim validation without evidence.

- **The specification** lives in [`draft/`](draft/). The
`draft-acosta-crypto-agility-manifest-01.xml` file is the authoritative source
(xml2rfc v3, RFC 7991 vocabulary); the `.md` file is a readable rendering kept in
sync with it. If you change one, change the other.
- **The contract** lives in [`schema/crypto-agility.schema.json`](schema/crypto-agility.schema.json)
(JSON Schema, draft 2020-12). The schema, the draft's Field Definitions section, and
the example in [`examples/`](examples/) must always agree.
## Validation

## How to propose a change

1. Open an issue first for anything substantive (a new field, a changed requirement, a
security consideration). It is cheaper to agree on a change in prose before editing
three files.
2. For an accepted change, open a pull request that updates **all** of: the XML draft,
the Markdown rendering, the JSON Schema, and the example (whichever apply), so they
never drift apart.
3. Keep the tone level and non-hype. This document competes on being correct and
honest about what it is (a claim, not a proof), not on marketing.

## House rules

- **No em dashes** anywhere: in prose, code, or XML. Use commas, colons, parentheses,
or a spaced hyphen instead.
- **Verify, do not invent.** Every external standard cited must be checked against its
primary source, with a correct title, number, and link. Do not add a citation you
have not read.
- **Cite normatively vs informatively with care.** RFC 2119 / 8174 / 8259 / 3339 / 8615 /
9110 and CycloneDX CBOM are normative (the draft depends on them to be implementable). The
crypto-agility survey references (RFC 7696, NIST CSWP 39, NIST IR 8547, NSM-10,
ISO/IEC 23837, NIST SP 800-56C) are informative.

## Validating your change locally

The example is checked against the schema with no third-party dependencies. Any small
JSON-Schema (draft 2020-12) validator works; if you have `ajv` available you can run it
directly, otherwise a short structural check over the schema's `required`, `type`,
`const`, and `additionalProperties` keywords is sufficient. Confirm that
`examples/crypto-agility.json` still validates after any schema change.

Check the draft XML is well-formed before opening a PR:
Install the pinned dependencies from `requirements.txt`, then run:

```sh
xmllint --noout draft/draft-acosta-crypto-agility-manifest-01.xml
python -m unittest discover -s tests -v
python -m tools.render_draft --check
xml2rfc --text draft/draft-acosta-crypto-agility-manifest-02.xml --out /tmp/crypto-agility-02.txt
```

## Licensing and IETF Trust provisions

The repository (schema, examples, tooling notes) is licensed under **Apache-2.0**; see
[LICENSE](LICENSE).

The Internet-Draft in [`draft/`](draft/) is a different matter. It carries the IETF
Trust legal provisions indicated by its `ipr="trust200902"` attribute, **regardless of
the repository's Apache-2.0 license**. By contributing changes to the draft you agree
that those contributions may be submitted to the IETF under the IETF Trust's terms
(see the IETF "Note Well" and BCP 78/79). If you are not comfortable with that,
contribute to the schema, examples, and tooling instead, and leave the draft prose to
be edited separately.
Run the independent consumer and live TLS checks required by CI. Required tests
must fail when their dependency or capability is absent, not silently skip.
Submission also requires review of current IETF author guidance, author contact,
revision availability, idnits output and any active submission deadline. Do not
submit or declare readiness while acceptance requirements remain unproven.

## Before an IETF submission
## Licensing

The draft is kept in submission-ready shape: the author contact
(`[email protected]`) and the IANA change-controller (`IETF`) are set. Validate the
XML with `xmllint --noout` and, ideally, render it through `xml2rfc` before submitting a
new revision, so the datatracker upload does not bounce on a format error.
Code, schemas and examples use Apache-2.0. Draft contributions are subject to the
IETF Trust provisions indicated by `ipr="trust200902"` and BCP 78/79. Do not publish
secrets or private inventories as evidence.
171 changes: 51 additions & 120 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,128 +1,59 @@
# Crypto-Agility Manifest

A discoverable, machine-readable JSON document that a website or repository publishes
at `/.well-known/crypto-agility.json` to declare its cryptographic posture, so an
agent, a CI bot, or an auditor can read it the way it already reads `security.txt`.

> **Status: proposal, not a ratified standard.** This repository holds an individual
> Internet-Draft and a reference schema. It is not an IETF working-group product, it
> does not represent IETF consensus, and nothing here is a standard. It is published
> to be reviewed, implemented, and argued with.

## What this is

The crypto-agility manifest is a small, versioned JSON object served from a well-known
URL. It summarizes, at a glance:

- **posture**: a readiness score, whether hybrid post-quantum key exchange is in use,
a count of quantum-vulnerable findings by severity, and how many are exposed to
harvest-now-decrypt-later;
- **cbomSummary**: a compact digest of, and a pointer to, the full CycloneDX
Cryptography Bill of Materials (CBOM) the summary was derived from;
- **attestation** (optional): a link to a machine-verifiable posture credential;
- **policy**: the migration deadlines the project declares it is measured against.

A worked, schema-valid example is in [`examples/crypto-agility.json`](examples/crypto-agility.json),
and the field-by-field contract is in [`schema/crypto-agility.schema.json`](schema/crypto-agility.schema.json).

## What this is not

- **Not a proof.** A manifest is a *self-reported claim*. Serving one says what a
project asserts about its posture, not what is true of it. The optional
`attestation` link is what turns a claim into something a third party can verify.
- **Not a replacement for CBOM.** [CycloneDX CBOM](https://cyclonedx.org/capabilities/cbom/)
is the inventory format that answers "what cryptography is in here". The manifest is
its public-facing *discovery* counterpart: a small summary at a predictable URL that
answers "where is this project's posture, and what does it summarize to". The
manifest carries a pointer (`cbomSummary.serialNumber`) to the full CBOM, it does
not inline it.
- **Not a new cryptographic mechanism, attestation format, or scoring method.** It
reuses existing pieces and only defines the well-known URI, the media type, and the
JSON fields.

## What it builds on

Crypto-agility as a concept is already well-standardized. This proposal does not
re-invent it; it adds one missing piece (a discoverable public posture manifest) on
top of the following work:

| Reference | What it gives us |
|---|---|
| [RFC 9116](https://www.rfc-editor.org/info/rfc9116) - A File Format to Aid in Security Vulnerability Disclosure (`security.txt`) | The **structural model** this manifest follows: a small file at a well-known location, discoverable from an origin alone. |
| [RFC 8615](https://www.rfc-editor.org/info/rfc8615) - Well-Known URIs | The `/.well-known/` mechanism and the IANA registry the `crypto-agility.json` suffix is proposed for. |
| [RFC 9110](https://www.rfc-editor.org/info/rfc9110) - HTTP Semantics (STD 97) | The `Content-Type` / media-type framework (`application/json`) used to serve the manifest. |
| [RFC 7696](https://www.rfc-editor.org/info/rfc7696) - Guidelines for Cryptographic Algorithm Agility (BCP 201) | The IETF guidance on algorithm agility this posture is measured against. |
| [NIST CSWP 39](https://doi.org/10.6028/NIST.CSWP.39) - Considerations for Achieving Cryptographic Agility: Strategies and Practices (Dec 2025) | The organizational and lifecycle framing of crypto-agility. |
| [CycloneDX CBOM](https://cyclonedx.org/capabilities/cbom/) (in CycloneDX 1.6 / 1.7, published as ECMA-424) | The **inventory format** the manifest summarizes and links to. |
| [NIST SP 800-56C Rev. 2](https://doi.org/10.6028/NIST.SP.800-56Cr2) - Key-Derivation Methods in Key-Establishment Schemes | Key-establishment context for hybrid key exchange. |
| [ISO/IEC 23837-1:2023 and 23837-2:2023](https://www.iso.org/standard/77097.html) - Security requirements and test methods for quantum key distribution | Standardized evaluation context for quantum-era key exchange. |
| [NSM-10](https://www.quantum.gov/security/) - National Security Memorandum 10 (May 2022) | The policy driver: transition to quantum-resistant cryptography, mitigating quantum risk to the maximum extent feasible by 2035. |
| [NIST IR 8547](https://csrc.nist.gov/pubs/ir/8547/ipd) - Transition to Post-Quantum Cryptography Standards (Initial Public Draft) | The default migration timeline (deprecate after 2030, disallow after 2035) the `policy` block cites. |

## The problem it solves

AI coding agents and CI systems increasingly make dependency and cryptography choices,
and auditors increasingly want a machine-checkable posture signal. Today there is no
standard place for any of them to learn a project's crypto stance, so every
integration is bespoke. The manifest gives them one predictable URL and one small,
stable schema to read, the same convention `robots.txt` and `security.txt` established
for their domains.

## Producing a manifest

The reference implementation lands in [qScan](https://github.com/quantakrypto/pqc-tools)
(pqc-tools): an emitter that produces a manifest from a scan, and a validator for a
local one, offline:
An individual Internet-Draft for discovering declared cryptographic capabilities at
`/.well-known/crypto-agility.json`. This is a proposal, not an IETF standard.
A manifest is a claim. Neither a digest, a signature nor a compatibility result
proves that a system is secure or quantum resistant.

## Version 2 candidate

The [-02 XML](draft/draft-acosta-crypto-agility-manifest-02.xml) is authoritative for
the v2 candidate. The [readable draft](draft/draft-acosta-crypto-agility-manifest-02.md),
[JSON Schema](schema/crypto-agility-v2.schema.json) and
[example](examples/crypto-agility-v2.json) accompany it. Version 1 remains unchanged
for existing qScan producers. Unknown versions require an explicit consumer upgrade.

The core describes subjects, capabilities, device profiles, evidence and migration
status. It separates declarations, observations and independent verification.
Optional evidence collection and monitoring belong to implementations, not a new
cryptographic protocol. The draft does not authorize configuration changes.

## Reference tools

- `tools.verifier`: bounded schema and semantic assessment, with no implicit trust
- `tools.tls_probe`: real TLS 1.3 negotiation through OpenSSL, certificate and hostname
validation, negotiated group and peer signature reporting
- `tools.observatory_ingestion`: signed collector evidence, endpoint binding,
transcript digest, freshness and provenance validation
- `tools.attestation`: compact JWS/JWT verification with pinned issuer policy,
explicit audience and purpose, expiry and persistent replay rejection
- `tools.discovery`: bounded HTTPS discovery and conditional caching
- `tools.qscan`: deterministic offline adaptation of existing qScan v1 output
- `tools.monitor`: repeated assessment, regression alerts and protected local state

Ed25519 in the reference JWS profile is classical. A TLS hybrid key exchange does
not establish post-quantum authentication. Device compatibility is a conservative
comparison of declarations, not a device certification or fleet measurement.

## Validation

```sh
# Emit the manifest to the conventional path (regenerate this in CI):
qscan . --crypto-agility -o .well-known/crypto-agility.json

# Equivalent explicit subcommand, with an attestation link, a hybrid-KEX
# assertion, and an operator policy:
qscan crypto-agility emit . \
--attestation https://quantakrypto.com/attest/acme \
--hybrid-kex \
--policy ./crypto-policy.json \
-o .well-known/crypto-agility.json

# Validate a local manifest against the schema (exit 0 = valid):
qscan crypto-agility validate ./.well-known/crypto-agility.json
```

Emitting is additive: it runs a scan, derives the manifest, and always exits 0, so
publishing a posture document never fails CI. The emitter and validator are strictly
offline; they never fetch a remote manifest or the `attestation.url`. Fetching and
verifying a *remote* manifest, and the credential behind its attestation link, is a
consumer-side or website-side step.

You do not need qScan to participate: any tool that writes a document conforming to
[`schema/crypto-agility.schema.json`](schema/crypto-agility.schema.json) and serves it
at `/.well-known/crypto-agility.json` is emitting a valid manifest.

## Repository layout

python -m pip install -r requirements.txt
npm ci --ignore-scripts
python -m unittest discover -s tests -v
python -m tools.verifier examples/crypto-agility-v2.json --now 2026-10-12T00:00:00Z
xml2rfc --no-network --text draft/draft-acosta-crypto-agility-manifest-02.xml
```
draft/ the Internet-Draft (xml2rfc v3 source + a readable Markdown rendering)
schema/ JSON Schema (draft 2020-12) for the manifest, version 1
examples/ a realistic, schema-valid example manifest
```

- [`draft/draft-acosta-crypto-agility-manifest-01.xml`](draft/draft-acosta-crypto-agility-manifest-01.xml)
- the authoritative xml2rfc v3 source.
- [`draft/draft-acosta-crypto-agility-manifest-01.md`](draft/draft-acosta-crypto-agility-manifest-01.md)
- a readable rendering for GitHub.

## Status and how to contribute

This is an early individual proposal. Review, issues, and pull requests are welcome,
especially on the field definitions, the security and operational considerations, and
the IANA registration. See [CONTRIBUTING.md](CONTRIBUTING.md).
The example intentionally reports fallback risks and the verifier exits 1.
Hosted CI requires actual hybrid TLS and pinned qScan interoperability; missing
runtimes are failures there. It retains logs, the exact tested commit and rendered
draft artifacts. See [implementation status](docs/implementation-status.md) for
acceptance evidence and [IETF readiness](docs/ietf-readiness.md) for submission gates.

Note that the Internet-Draft itself carries IETF Trust legal provisions (`trust200902`)
regardless of the repository's Apache-2.0 license; contributing changes to the draft
means agreeing those changes may be contributed under the IETF Trust's terms. The
repository code, schema, and examples are licensed under [Apache-2.0](LICENSE).
No production deployment or Internet-Draft submission is implied by passing the
reference tests. In particular, legacy unsigned measurements cannot be relabeled
as authenticated evidence.

The draft lists `[email protected]` as the author contact. Correspondence about the
proposal is welcome there or via issues on this repository.
Code is Apache-2.0. Internet-Drafts carry IETF Trust provisions (`trust200902`).
Author contact: `[email protected]`. See [CONTRIBUTING.md](CONTRIBUTING.md).
Loading