diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f8ccd66..aa4b7a1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,15 +1,8 @@ # Contributing -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`. +This is an individual proposal, not an IETF standard. Preserve supported input compatibility and reject unsupported contract versions explicitly. The authoritative specification 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`. -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. +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. ## Validation @@ -21,14 +14,8 @@ python -m tools.render_draft --check xml2rfc --text draft/draft-acosta-crypto-agility-manifest-02.xml --out /tmp/crypto-agility-02.txt ``` -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. +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. Submission status must reflect an observed Datatracker result. Authorize the submission explicitly, complete the current checks, and preserve the submission receipt; never infer publication from local rendering or upload preparation. ## Licensing -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. +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. diff --git a/README.md b/README.md index 0e7d6f6..5c3e957 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,40 @@ # Crypto-Agility Manifest -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 +A discoverable JSON contract for declared cryptographic capabilities, migration plans and evidence references at `/.well-known/crypto-agility.json`. This is an individual Internet-Draft proposal, not an IETF standard. A declaration, digest, signature or compatibility result does not prove comprehensive security or quantum resistance. + +## Current contract + +The [authoritative RFCXML](draft/draft-acosta-crypto-agility-manifest-02.xml), [readable specification](draft/draft-acosta-crypto-agility-manifest-02.md), [JSON Schema](schema/crypto-agility-v2.schema.json), [minimal example](examples/minimal-v2.json) and [complete example](examples/crypto-agility-v2.json) define the contract with `version: 2`. The Internet-Draft revision suffix and the JSON contract version are separate identifiers. Consumers reject unsupported versions; the [qScan adapter](docs/qscan-integration.md) explicitly handles supported input with `version: 1` without treating source declarations as runtime evidence. + +A manifest identifies one independently authorized subject. It contains generation and expiry timestamps, declared endpoint profiles, observations and migration records, with optional evidence, policies and exceptions. Evidence is bound to its exact bytes and subject. Declarations, authenticated collector statements and independent measurements are distinct assurance levels. Key establishment and authentication are assessed separately. The contract grants no authority to change a system. + +## Reference components + +| Component | Responsibility | +| --- | --- | +| `tools.verifier` | Bounded schema and semantic assessment; no implicit trust or passing security certification | +| `tools.tls_probe` | Actual TLS 1.3 negotiation with certificate and reference-identity validation | +| `tools.observatory_ingestion` | Authenticated collector provenance, endpoint, transcript and freshness checks | +| `tools.attestation` | Compact JWS verification, explicit issuer/key/subject/audience/purpose policy and durable replay rejection | +| `tools.discovery` | Allowlisted HTTPS retrieval, bounded conditional caching and exact response bytes | +| `tools.qscan` | Deterministic offline adaptation of supported qScan source evidence | +| `tools.monitor` | Repeated assessment, historical regression baseline and durable alert occurrences | +| `tools.consumer` | Immutable bundle selection, local trust/status enforcement and persistent supervision | +| `tools.kms_status` | Bounded authenticated internal key-status refresh using an existing identity | +| `tools.alert_delivery` | Persistent delivery intent, provider receipt verification and exact acknowledgment | + +The reference signature profile supports Ed25519 and ML-DSA-65 under independently provisioned algorithm/key policies. Ed25519 is classical. A hybrid TLS exchange does not establish post-quantum certificate authentication. Declared compatibility is a conservative profile comparison, not representative device execution or fleet certification. + +## Use and validation + +Install `requirements.txt` and `npm ci --ignore-scripts`, then follow [implementation status](docs/implementation-status.md) for the complete required test environment. A deterministic example assessment is: ```sh -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 +python -m tools.render_draft --check xml2rfc --no-network --text draft/draft-acosta-crypto-agility-manifest-02.xml ``` -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. - -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 example intentionally reports fallback risks and exits 1. Read [runtime evidence](docs/runtime-evidence.md), [signed observations](docs/signed-observations.md), [collector integration](docs/observatory-integration.md), [consumer deployment](docs/consumer-deployment.md) and the [security review](docs/security-review.md) before relying on evidence. Production acceptance and its limits are in [implementation status](docs/implementation-status.md); submission status and requirements are in [IETF readiness](docs/ietf-readiness.md). -Code is Apache-2.0. Internet-Drafts carry IETF Trust provisions (`trust200902`). -Author contact: `leon@quantakrypto.com`. See [CONTRIBUTING.md](CONTRIBUTING.md). +Code, schemas and examples use Apache-2.0. Internet-Drafts carry IETF Trust provisions (`trust200902`). Author contact: `leon@quantakrypto.com`. See [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/docs/change-feed-design.md b/docs/change-feed-design.md index 0ecc935..8fd6824 100644 --- a/docs/change-feed-design.md +++ b/docs/change-feed-design.md @@ -1,31 +1,11 @@ # Discovery and optional refresh hints -The only interoperable discovery resource in this candidate is the current manifest -at `/.well-known/crypto-agility.json`. No custom feed format, hash chain, sequence -protocol, or push service is required. Those earlier proposals added state and trust -problems without demonstrating a benefit over conditional HTTP retrieval. +The discovery resource is the current manifest at `/.well-known/crypto-agility.json`. No custom feed format, hash chain, sequence protocol, or push service is required. Conditional HTTP retrieval provides bounded refresh without a second trust or sequencing protocol. -`tools.discovery.DiscoveryClient` uses an explicit origin allowlist, verified HTTPS, -public-address checking followed by a pinned-IP connection with hostname/SNI -validation, no proxies and no redirects. A worker deadline bounds DNS, TLS, response -headers and body reads together. Responses are limited to 1 MiB, uncompressed JSON, -16 levels of nesting and schema limits. Duplicate members and non-JSON numbers fail. +`tools.discovery.DiscoveryClient` uses an explicit origin allowlist, verified HTTPS, public-address checking followed by a pinned-IP connection with hostname/SNI validation, no proxies and no redirects. A worker deadline bounds DNS, TLS, response headers and body reads together. Responses are limited to 1 MiB, uncompressed JSON, 16 levels of nesting and schema limits. Duplicate members and non-JSON numbers fail. -The memory-only cache supports ETag/If-None-Match and Last-Modified/If-Modified-Since, -Cache-Control max-age, no-cache, no-store and Age/Date. Local max-age defaults to -300 seconds and cannot exceed 3600 seconds. There is at most one cached manifest -per configured origin, at most 256 origins. An expired or mismatched manifest is -rejected, including after 304. HTTP freshness never refreshes evidence timestamps: -the monitoring caller must reassess evidence and signatures on every evaluation. -The cache returns original bytes for exact snapshot digest binding. +The memory-only cache supports ETag/If-None-Match and Last-Modified/If-Modified-Since, Cache-Control max-age, no-cache, no-store and Age/Date. Local max-age defaults to 300 seconds and cannot exceed 3600 seconds. There is at most one cached manifest per configured origin, at most 256 origins. An expired or mismatched manifest is rejected, including after 304. HTTP freshness never refreshes evidence timestamps: the monitoring caller must reassess evidence and signatures on every evaluation. The cache returns original bytes for exact snapshot digest binding. -An operator can arrange a local authenticated notification to call -`fetch(configured_origin, expected_subject, force=True)`. The notification is only a -refresh hint. Its contents never supply a URL, security finding, or instruction. -Ordinary periodic polling recovers from lost, duplicated, reordered, or forged -notifications. A separate feed protocol should only be considered after an actual -interoperability use case demonstrates that HTTP validators are insufficient. +An operator can arrange a local authenticated notification to call `fetch(configured_origin, expected_subject, force=True)`. The notification is only a refresh hint. Its contents never supply a URL, security finding, or instruction. Ordinary periodic polling recovers from lost, duplicated, reordered, or forged notifications. A separate feed protocol should only be considered after an actual interoperability use case demonstrates that HTTP validators are insufficient. -Use `python -m unittest discover -s tests -p 'test_discovery.py' -v` for cache, -expiry, SSRF, redirect, malformed response, exact hostname validation and total -worker deadline tests. These tests do not claim production network availability. +Use `python -m unittest discover -s tests -p 'test_discovery.py' -v` for cache, expiry, SSRF, redirect, malformed response, exact hostname validation and total worker deadline tests. These tests do not claim production network availability. diff --git a/docs/consumer-deployment.md b/docs/consumer-deployment.md index fd00a7f..87542bf 100644 --- a/docs/consumer-deployment.md +++ b/docs/consumer-deployment.md @@ -76,7 +76,7 @@ An operator can replace the manually asserted `keyStatus` object with the follow The independently provisioned withdrawal file contains `{"version":1,"withdrawn":false,"reason":""}`. Changing `withdrawn` to `true` immediately fails the next consumer check; a missing/malformed file fails closed. The refresher never writes this override. Retain the local override independently of downloaded receipts, bundles and deployment releases. -Run `python -m tools.kms_status --policy /private/kms-policy.json --output /private/kms-status.json` in the existing authorized signing environment. The policy contains exactly `version: 1`, `keyArn`, `spkiSha256` and `ttlSeconds`. Use an explicit key ARN, never an alias. A 15-minute workflow interval with `ttlSeconds: 1800` leaves one refresh interval of margin; the maximum accepted receipt lifetime is one hour. Optional `--profile qk-attest` selects an already configured local profile. Actions should inherit its existing AWS authentication and omit the profile. No signing API is called and no credentials are read out, copied, exported to the consumer or installed on its host. +Run `python -m tools.kms_status --policy /private/kms-policy.json --output /private/kms-status.json` in the existing authorized signing environment. The policy contains exactly `version: 1`, `keyArn`, `spkiSha256` and `ttlSeconds`. Use an explicit key ARN, never an alias. A 15-minute workflow interval with `ttlSeconds: 1800` leaves one refresh interval of margin; the maximum accepted receipt lifetime is one hour. Optional `--profile existing-profile` selects an already configured local profile. Actions should inherit its existing AWS authentication and omit the profile. No signing API is called and no credentials are read out, copied, exported to the consumer or installed on its host. The bounded CLI invokes authenticated `DescribeKey` and `GetPublicKey` against the fixed regional HTTPS KMS endpoint. It requires the exact ARN, enabled state, `ML_DSA_65`, `SIGN_VERIFY`, `ML_DSA_SHAKE_256`, valid ML-DSA-65 SPKI framing and the approved SHA-256 digest. AWS documents the SPKI and algorithm fields in [GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html). Requests that fail or disagree replace the receipt with `unknown` and return a nonzero exit code. Transfer that unknown receipt too; do not conceal a disabled key by publishing only successful refreshes. Invalid local policy or an unwritable output path stops publication; an old receipt expires naturally and cannot remain valid indefinitely. @@ -86,7 +86,7 @@ This internal policy establishes that the authorized refresh observed the expect ## Durable email delivery -The approved QuantaKrypto alert destination is `leon@quantakrypto.com`. Provision owner-private `delivery.json` with `{"version":1,"recipient":"leon@quantakrypto.com","sender":"quantakrypto "}` after confirming the existing sender configuration. Honor a configured production `EMAIL_FROM` when provisioning this sender instead of assuming the default. The adapter uses the existing `RESEND_API_KEY` from its runtime environment; it does not create, copy or print the key. Configure the service to read the existing host environment in place using a reviewed host-specific `EnvironmentFile` drop-in or an existing launcher that passes only the needed variable. Protect that existing source file with owner-only permissions before activation. The public contact form and its automatic confirmation are not a monitor transport. +Obtain the operator-approved recipient and sender before activating delivery. Provision owner-private `delivery.json`, for example `{"version":1,"recipient":"alerts@example.org","sender":"Monitor "}`, using the actual approved addresses. Honor a configured production `EMAIL_FROM` when provisioning this sender instead of assuming the default. The adapter uses the existing `RESEND_API_KEY` from its runtime environment; it does not create, copy or print the key. Configure the service to read the existing host environment in place using a reviewed host-specific `EnvironmentFile` drop-in or an existing launcher that passes only the needed variable. Protect that existing source file with owner-only permissions before activation. The public contact form and its automatic confirmation are not a monitor transport. Explicitly initialize delivery once with `python -m tools.alert_delivery --initialize-delivery --state-directory /absolute/private/state`, after initializing the consumer state. This adds `delivery.json` and its stable lock inside the persistent state directory. Initialization refuses previous or interrupted delivery state. Missing delivery state after initialization is an error; never delete it to retry a send. Run `python -m tools.alert_delivery --config /absolute/private/config/delivery.json --state-directory /absolute/private/state` from the existing mail runtime environment. The service/timer templates run independently from the offline consumer, so mail failures do not stop measurement assessment. diff --git a/docs/cryptographic-security-review-iteration-2.md b/docs/cryptographic-security-review-iteration-2.md deleted file mode 100644 index e54f2f9..0000000 --- a/docs/cryptographic-security-review-iteration-2.md +++ /dev/null @@ -1,26 +0,0 @@ -# Security review - -Threats considered: forged or replayed collector statements, wrong-subject evidence, -stale measurements in fresh wrappers, algorithm confusion, offered-group spoofing, -invalid certificates, malicious HTTP destinations, resource exhaustion, local state -symlinks and concurrent writers. The reference tests exercise these boundaries. - -The implementation uses standard TLS certificate/hostname verification and compact -JWS/JWT. It does not create a signature canonicalization algorithm, hybrid combiner, -Merkle construction or new certificate-validation mechanism. Ed25519 statement -signatures are classical; hybrid TLS key establishment says nothing by itself about -post-quantum authentication. - -Declared capabilities remain declarations. Authenticated observations mean an -explicitly trusted collector signed scoped evidence. Independently verified security -requires corroborating measurements and an explicit relying-party decision; a -publisher cannot select this status in a manifest. - -State is protected from partial writes, concurrent writers, permissive file modes, -symlinks and observable rollback. It cannot defend against the account owner, root, -loss of the state directory or restored disk images. An operator must retain and -recover trusted checkpoints. No alert delivery is claimed without a delivery receipt. - -Scope decisions and remaining deployment blockers are recorded in -[implementation-status.md](implementation-status.md). Source-level security review -and passing adversarial tests are not a cryptographic certification or an IETF review. diff --git a/docs/ietf-readiness.md b/docs/ietf-readiness.md index a750e0e..3190b9a 100644 --- a/docs/ietf-readiness.md +++ b/docs/ietf-readiness.md @@ -1,23 +1,21 @@ -# IETF -02 readiness +# Internet-Draft publication and readiness -**Revision -02 has not been submitted.** It remains an individual proposal, not an IETF standard or an endorsed implementation. Formal submission preparation and this project's production/interoperability acceptance goals are separate below. +The complete proposal is `draft-acosta-crypto-agility-manifest-02`. Consult the [Datatracker publication record](https://datatracker.ietf.org/doc/draft-acosta-crypto-agility-manifest/) for the accepted revision and submission state. Local source files, an upload or passing checks do not establish publication. This is an individual Informational proposal, not an IETF standard or endorsement. -## Prepared materials +## Submission materials -The authoritative RFCXML v3, generated Markdown, rendered text, embedded JSON Schema and examples describe one v2 contract. Version 1 remains available for existing qScan producers. The proposal requests a provisional well-known suffix; it introduces no media type, signature format, feed protocol or cryptographic algorithm. Contributor instructions require complete schema validation and regeneration checks. +The authoritative RFCXML v3, generated Markdown, rendered text, embedded JSON Schema and examples define the complete contract with JSON `version: 2`. The RFCXML `docName` and series revision identify the Internet-Draft submission, separately from that machine-readable contract version. A supported qScan input adapter is documented independently. The proposal requests provisional registration of a well-known suffix; it defines no new media type, signature format, feed protocol or cryptographic algorithm. -## Submission process checked on 2026-10-13 +Check author metadata, date, references, complete schema/example consistency, standalone XML and renderings on the exact committed submission tree. Review idnits diagnostics and resolve blocking submission-tool errors. Hosted CI on that tree is a project release requirement. Offline idnits success is preparation evidence, not a submission-service receipt. -The [Datatracker record](https://datatracker.ietf.org/doc/draft-acosta-crypto-agility-manifest/) still lists -01 as an individual Internet-Draft; -02 is the intended next revision. Its published status explicitly does not imply IETF endorsement. +## Submission procedure -The [official author guidance](https://authors.ietf.org/submitting-your-internet-draft) prefers RFCXML v3 and describes submission through the [Datatracker submission tool](https://datatracker.ietf.org/submit/). Posting requires the requested author confirmation unless the submitter is authenticated and listed as an author. The [IETF 127 dates](https://datatracker.ietf.org/meeting/127/important-dates/) list the Internet-Draft cutoff as November 2, 2026 at 23:59 UTC. Recheck these sources immediately before submitting; no submission transaction or author confirmation has been attempted for -02. +The [official author guidance](https://authors.ietf.org/submitting-your-internet-draft) prefers RFCXML v3 submitted through the [Datatracker submission tool](https://datatracker.ietf.org/submit/). Posting requires author confirmation unless the submitter is authenticated and listed as an author. Before upload, confirm the next available revision from the publication record and check the current [IETF important dates](https://datatracker.ietf.org/meeting/127/important-dates/). The dates checked on October 13, 2026 list November 2, 2026 at 23:59 UTC as the IETF 127 Internet-Draft cutoff; recheck at submission time. -Before an upload, verify the revision, author metadata, references, standalone XML and generated renderings; review the exact candidate's idnits diagnostics and resolve blocking submission-tool errors. Hosted CI must pass on the final committed tree as this project's release policy. Passing offline idnits is preparation evidence, not a receipt from the submission service. +Review the service's extracted metadata and validation results before posting. Retain the accepted publication URL and confirmation result after submission. A pending confirmation or failed upload must remain explicitly pending or failed; neither may be described as published. Verify the service-extracted metadata before posting and preserve any confirmation requirement until it is satisfied. -## Project acceptance and quality goals +## Engineering assurance versus formal upload requirements -The [dated implementation status](implementation-status.md) records the narrower evidence actually observed. October 11 established the stricter unsigned collector and durable legacy issuance. October 13 added fresh standard-JWS own-origin issuance, authenticated KMS status under the internal trust policy, exact manifest binding, persistent consumer restart/replay rejection and fresh-bundle recovery. A labeled test obtained provider-reported delivery. Independent verification also checked the public JWS, exact manifest/transcript bytes and ledger history. The consumer and alert timer are enabled; retained production alerts obtained durable provider-reported delivered receipts before exact acknowledgment. These receipts are bounded production evidence, not proof of every implementation or device. +[Implementation status](implementation-status.md) states the current operational contract, bounded production receipts and remaining assurance limits. Independent complete semantic consumers, representative device execution and independent IETF-area feedback are project quality goals. The individual Internet-Draft submission guidance does not impose production deployment or independent implementation as upload prerequisites; filing a draft does not establish them either. -Independent complete semantic consumers, representative device execution and independent IETF-area feedback remain project quality goals. Node/OpenSSL/Python wire tests establish a narrower interoperability boundary. The individual Internet-Draft submission guidance does not require production deployment, heterogeneous device measurements or prior independent implementation; those must not be presented as formal IETF upload requirements. Equally, filing an Internet-Draft would not establish those engineering properties or grant standards status. - -The internal KMS status policy is not public root-signed nonrevocation. Compatibility stays unknown or declared where representative execution is absent. Prepare the submission materials while retaining these limitations; do not submit until the user's technical-readiness condition and current submission checks have been reviewed. No submission or IETF endorsement is claimed. +The authenticated consumer separates key establishment from authentication, declarations from measurements, and signature authenticity from independent measurement truth. Its internal KMS status policy is not public root-signed nonrevocation. Compatibility remains unknown or declared without representative execution. Preserve these limitations in the submitted material and subsequent operational claims. diff --git a/docs/implementation-status.md b/docs/implementation-status.md index 6beafba..bacf961 100644 --- a/docs/implementation-status.md +++ b/docs/implementation-status.md @@ -1,24 +1,35 @@ -# Implementation and acceptance +# Implementation and operational assurance -The v2 reference implementation and persistent consumer are merged. The workflow tests the PR head commit explicitly and retains logs and draft renderings as `validation-` artifacts. The [consumer runtime checks](https://github.com/quantakrypto/crypto-agility/actions/runs/38382277746) passed on `7c0c43cc54c721b8a835ebaf6241cb735d4b9f7e`, merged as `3a459dd`. Production receipts below are dated observations, not a certification of every deployment or device. +The reference implementation provides bounded manifest discovery and assessment, authenticated collector ingestion, declared compatibility comparison and persistent monitoring. Its tests exercise the contract with actual TLS handshakes, independently generated JWS, multiple runtimes and a pinned qScan producer. Passing those tests establishes the tested behavior, not comprehensive security or certification of a deployment. -| Requirement | Reference implementation and evidence | Remaining acceptance boundary | +## Implementation boundary + +| Component | Implemented behavior | Assurance limit | | --- | --- | --- | -| Authenticated ingestion | JWS issuer policy, endpoint/subject binding, transcript digest, provenance and freshness; ingestion and live pipeline tests | October 13 own-origin observations authenticate in the deployed consumer; independent verification checked the public JWS, exact manifest/transcript bytes and ledger binding. Historical full-panel telemetry remains unsigned | -| TLS hybrid and fallback | Mandatory local OpenSSL 3.5.9 handshakes; hybrid, classical, deny-classical, invalid CA, wrong name, expired certificate, TLS 1.2 rejection | Tests cover the pinned TLS stack and fixture endpoints, not every provider, client or CDN edge | -| Signed attestations | Standard compact JWS/JWT, explicit algorithm policy, trusted issuer/key allowlists, revocation, audience, expiry, durable replay state; independent Node/OpenSSL/Python wire tests | Existing-key RFC 9964 ML-DSA-65 issuance and fresh authenticated KMS status passed. Internal key-status policy is not public root-signed nonrevocation or independent measurement truth | -| Compatibility | Conservative profile comparison across supported device classes, per-hop scope and constraint handling | Declared compatibility is not execution on browsers, mobile hardware, gateways or constrained devices; no fleet enrollment/coverage claim | -| Monitoring | Repeated assessments, stable actionable alert IDs, expiry/regression detection, atomic owner-only state, concurrent-writer and symlink tests | Deployed consumer passed singleton, restart/replay rejection and fresh-bundle recovery; alert timer is enabled; retained production alerts obtained persisted provider delivery receipts before acknowledgment | -| Discovery | HTTPS certificate validation, origin allowlist, pinned public IP, total deadline/size limits, ETag/Last-Modified, no stale-on-error | Optional change notifications only trigger a snapshot refresh; no unnecessary feed protocol | -| qScan | Pinned real qScan v1 producer/scan, repeatable bytes and v2 adapter with exact evidence digest and repository/commit binding | Native qScan remains v1; the merged reference implementation supplies the tested adapter rather than changing its release | -| Tests | Unit, adversarial, real TLS, Node/Python interoperability, qScan integration, concurrency and bounded performance gates | Structural schema and JWS interoperability do not establish independent full semantic implementations or device interoperability | -| Draft | Self-contained -02 XML, Markdown, schema, examples and offline text/HTML rendering | Formal submission checks are distinct from project interoperability and review goals; see IETF readiness | +| Manifest assessment | Bundled schema, bounded parsing, subject/reference checks, timestamps, contradictions and migration evidence requirements | `unverified` means no structural or semantic finding, not verified security | +| TLS collection | TLS 1.3 negotiation, selected group, certificate and reference-identity validation, bounded exact transcript | One address, time and probe configuration; no fleet coverage or PQC certificate authentication | +| Signed observations | Pinned Ed25519 or ML-DSA-65 policy, subject/audience/purpose binding, exact manifest/transcript digests, freshness and replay rejection | Authenticates a collector statement; no independent proof that the measurement is true | +| Compatibility | Conservative protocol and algorithm intersection, explicit constraints and single-hop scope | Device labels and declared compatibility do not establish runtime interoperability | +| Persistent consumer | Immutable complete bundles, separate local trust/status, singleton supervision, durable replay/clock/baseline state | Owner/root compromise or state rollback requires explicit operational recovery | +| Alert delivery | Persistent occurrence and send intent, bounded provider requests, exact receipt matching before acknowledgment | Provider-reported delivery is not proof of inbox visibility or human response | +| Discovery | Allowlisted HTTPS, validated certificates, pinned public IP, bounded conditional caching, no stale-on-error | Fresh HTTP responses never refresh evidence validity | +| qScan integration | Deterministic adapter for supported producer format with exact source bytes and repository/commit binding | Source scan findings remain inferred; no deployed device or live TLS assurance | + +The standalone verifier does not fetch or authenticate evidence. Signed evidence ingestion and the persistent consumer apply explicit additional policies. The collector profile requires whole-second UTC timestamps; manifest timestamps separately allow up to six fractional digits. Consumer acceptance never elevates a declared capability to independently verified runtime behavior. + +## Observed deployment acceptance + +The following receipt is scoped to the October 13, 2026 checks and the deployed own-origin observation path. It is not a continuous availability guarantee. The deployed consumer uses an independently pinned key, explicit origin/subject/audience authorization, fresh authenticated internal KMS status and a separate local withdrawal decision. Internal status is not public root-signed nonrevocation. Existing signing and publication share one serialized durable issuer ledger with preserved history. + +At 12:28:46 UTC, independent read-only verification checked the [public observation JWS](https://quantakrypto.com/.well-known/observations/eb01a9bbbb9fac37b1e7f215c8dc1608715f1f89616f7953c3947429daf17d7f/observation.jwt), [captured manifest](https://quantakrypto.com/.well-known/observations/eb01a9bbbb9fac37b1e7f215c8dc1608715f1f89616f7953c3947429daf17d7f/manifest.json), [TLS transcript](https://quantakrypto.com/.well-known/observations/eb01a9bbbb9fac37b1e7f215c8dc1608715f1f89616f7953c3947429daf17d7f/transcript.txt) and [public ledger](https://quantakrypto.com/.well-known/attestation-ledger.jsonl) against the pinned key and native consumer report. The observation reported certificate-validated TLS 1.3 with `X25519MLKEM768`, used ledger index 62 in a 63-entry ledger and preserved the previously captured 56-entry prefix. The [origin declaration](https://quantakrypto.com/.well-known/crypto-agility-origin.json) had SHA-256 `6fe2a44b51dc117818eec67b685d288d24a1caafeede7ebda24965f968cc2d26`. These URLs advance with later observations; the receipt time and index identify this check. The explicit origin declaration is a deployment-specific separate resource, not a replacement discovery suffix or a reinterpretation of a repository manifest. + +The persistent consumer rejected a concurrent second process and rejected an already-consumed JWT after restart. It preserved its trusted historical baseline and pending alerts, then recovered current authentication from a fresh bundle without clearing replay state. Three replay entries persisted during acceptance. Consumer and alert timer were enabled and active. At 12:29:34 UTC the delivery timer persisted matching provider-reported delivered receipts for all three retained production alerts before exact acknowledgment; the pending outbox was empty and receipt history remained available. Independent delivery review confirmed these observations. -## Scope and necessity +The assessment remains `needs-review` because fallback behavior is declared unknown. Representative browser, mobile, gateway and constrained-device measurements are absent, so those compatibility claims remain unknown or declared. Public full-panel telemetry is not authenticated merely because the own-origin path is signed. The [published methodology](https://quantakrypto.com/observatory/methodology) discloses TLS 1.3 and certificate-validation requirements; changed measurement eligibility must not be presented as adoption growth. A configured refresh interval is not evidence of sustained automatic scheduling. -Keep discovery small. No custom signature envelope, bespoke Merkle construction, new hybrid combiner, public feed hash chain, remediation executor or universal readiness score is required. A fresh HTTP representation does not refresh evidence. A signed observation authenticates its collector; independent verification must be reported separately. The manifest never supplies its own trust policy. +Deployment claims must identify the tested runtime and observed evidence. Every release requires exact-tree checks and deployment verification. -## Reproduce +## Reproduce required checks Install `requirements.txt` and `npm ci --ignore-scripts`. Build OpenSSL with `bash scripts/build-test-openssl.sh build/openssl`. Check out pqc-tools at `6a81b5537eaedf61f259334b4e430e580c35942c`, run `npm ci --ignore-scripts` and `npm run build` there, then: @@ -27,45 +38,11 @@ OPENSSL_BINARY="$PWD/build/openssl/apps/openssl" \ REQUIRE_HYBRID_TLS=1 REQUIRE_QSCAN_INTEROP=1 \ QSCAN_ROOT="$PWD/build/pqc-tools" \ python scripts/check-tests.py +python -m tools.render_draft --check ``` -Hosted CI must pass on the exact published commit. Local passing tests are not a substitute. The implementation PRs have merged. Project acceptance boundaries and formal Internet-Draft submission preparation are tracked separately in [ietf-readiness.md](ietf-readiness.md). - -## Historical production validation receipts (2026-10-11) - -These paragraphs preserve what was established on October 11. Statements about work not yet integrated describe that date; the October 13 section below supersedes their operational status. - -The original public implementation changes merged as crypto-agility `7b79cd2` and Observatory `238860e`. Independent engineering and security review corrected monitor alert-loss and baseline-loss failure paths: pending delivery IDs require explicit acknowledgment, and rejected evidence cannot erase the historical authenticated baseline or turn it into current assurance. - -The existing Observatory timer and service were updated to `238860e`; no second scheduler was created. Its full run from 18:41:47 to 19:00:31 UTC completed with exit status 0 and refreshed all 271 database rows. It retained 203 certificate-validated collector records in an owner-only directory (0700) with owner-only files (0600); filename and transcript hashes were checked. All 203 records remain unsigned. Successful collection and local hashes do not establish an authenticated signing handoff, subject authorization or exact manifest-bound issuance. - -The stricter TLS 1.3/certificate-validation methodology changed the measured web denominator from 70 to 66 while the hybrid count stayed at 48: `48/70` became `48/66`. This is a methodology effect, not adoption growth. The basic website methodology disclosure is live. The explicit cutover-date disclosure is now visible in the [website metrics methodology](https://quantakrypto.com/observatory/methodology). Device compatibility remains unknown or declared wherever representative execution evidence is absent. - -The existing-key KMS canary succeeded on October 11. It matched the historical online key SPKI fingerprint, requested a bounded RAW signature and verified it locally. Legacy issuance was skipped; no token or ledger entry was created. This demonstrates signing access and key continuity, not non-revocation or authorized observation issuance. - -### Durable legacy issuance - -The two legacy website writers were integrated with one serialized durable authority. The public ledger is a replica, and recovery journals and per-subject artifact state survive website asset swaps. - -Authenticated migration preserved the existing 51-entry ledger with full-file SHA-256 `3c0262b8b37edb4c1e8e3f81d254f907c35c759287fe3fafb507a4afab99f765`. Real production issuance increased the ledger to 52 entries, giving full-file SHA-256 `0ab2c5823eeb2d717f6e70c2c3695a04ded4810d28e8b17426ab9126c69c9bb0`. The canonical and served ledgers and all three artifact replicas matched, and no pending journal remained. The public legacy ML-DSA signature verified against SPKI pin `33698c1c345d1df7a5308c513ee5c3925bc7f31c12aeb883549430a6d8f7cc80`. - -These receipts cover the legacy writers only. Legacy v1 signs the qScan logical content hash and readiness value; retained byte checksums are storage checks, not signed exact manifest/evidence bindings. The standard-JWS exporter has not been integrated with this shared transaction, and a source attestation does not prove the corresponding build is the live runtime. - -## Current production validation (2026-10-13) - -The deployed consumer authenticated fresh own-origin observations at 12:11:23 UTC, after a controlled restart at 12:15:06 UTC, and following a website deployment at 12:28:06 UTC. The observations reported certificate-validated TLS 1.3 with `X25519MLKEM768`. They bind the exact [origin manifest](https://quantakrypto.com/.well-known/crypto-agility-origin.json) bytes, whose SHA-256 was `6fe2a44b51dc117818eec67b685d288d24a1caafeede7ebda24965f968cc2d26`. This explicit origin declaration is separate from the existing qScan repository declaration; neither is silently reinterpreted as the other. - -The standard observation issuer uses the existing signing identity and one serialized durable ledger shared with legacy issuance. Fresh authenticated KMS status, an independently pinned public key, explicit subject/audience authorization and a separate local withdrawal decision govern the consumer. The earlier status-read permission failure was resolved and the actual signing environment passed its status preflight. This internal operational policy does not establish public root-signed nonrevocation, an independent collector, PQC certificate authentication or device interoperability. - -The production supervisor rejected a concurrent second process. Restart rejected the already-consumed observation as replay, retained the historical authenticated baseline and pending alerts, and then accepted the second fresh bundle without resetting state. All three replay entries persisted. A labeled delivery test obtained a durable provider-reported delivered receipt before acknowledgment; this is not proof of inbox visibility or human action. The consumer and alert timer are enabled. At 12:29:34 UTC the timer persisted matching provider-reported delivered receipts for all three retained production alerts before acknowledging their exact occurrences; the pending outbox was empty and historical receipts remained retained. An initial public artifact HTTP 404 was corrected before the independent public verification below. - -An independent read-only verification at 12:28:46 UTC checked the [public observation JWS](https://quantakrypto.com/.well-known/observations/eb01a9bbbb9fac37b1e7f215c8dc1608715f1f89616f7953c3947429daf17d7f/observation.jwt), [captured manifest](https://quantakrypto.com/.well-known/observations/eb01a9bbbb9fac37b1e7f215c8dc1608715f1f89616f7953c3947429daf17d7f/manifest.json), [TLS transcript](https://quantakrypto.com/.well-known/observations/eb01a9bbbb9fac37b1e7f215c8dc1608715f1f89616f7953c3947429daf17d7f/transcript.txt) and [public ledger](https://quantakrypto.com/.well-known/attestation-ledger.jsonl) against the pinned key and native consumer report. The signed observation used ledger index 62 in the 63-entry ledger; the previously captured 56-entry prefix was unchanged. This independently verifies the signature and byte bindings, not the truth of a measurement performed by the same operator. These live URLs advance with later observations; the times and index above identify this receipt. - -The assessment remains `needs-review` because fallback behavior is declared unknown. Representative browser, mobile, gateway and constrained-device measurements have not been established; those compatibility claims remain unknown or declared. The 203 historical full-panel collector records described above remain unsigned; these new own-origin observations do not retroactively authenticate them. A configured refresh interval is not evidence that the scheduler has actually maintained that cadence. +Hosted CI tests the exact committed tree and retains logs and rendered draft artifacts. Required runtimes must be present; fixture success is not a production receipt. The [public validation workflow](https://github.com/quantakrypto/crypto-agility/actions/workflows/verify-v2.yml) records exact-commit results and artifacts. -### Remaining project acceptance and quality goals +## Ongoing assurance and submission -- Retain the observed production safeguards: public artifact byte binding, owner-private persistent state, local withdrawal, and acknowledgment only after confirmed provider delivery. These passed the dated checks above and require continued operation. -- Observe continued freshness and automatic refresh behavior without weakening expiry, replay or local-withdrawal checks. -- Demonstrate independent complete consumer interoperability and obtain independent IETF-area feedback. These are project quality goals, not formal prerequisites imposed by the individual Internet-Draft upload process. -- Recheck the final candidate's renderings, idnits diagnostics and current submission requirements before any submission. No -02 submission or IETF endorsement is claimed. +Continue monitoring freshness, public byte binding, local withdrawal, persistent state and delivery acknowledgments. Independent complete semantic implementations, representative device execution and IETF-area feedback are valuable project quality goals; wire interoperability and source review do not establish them. These goals are distinct from formal individual Internet-Draft upload requirements, which are described in [IETF readiness](ietf-readiness.md). No IETF endorsement is implied by implementation, deployment or submission. diff --git a/docs/observatory-integration.md b/docs/observatory-integration.md index fb33333..08db6a2 100644 --- a/docs/observatory-integration.md +++ b/docs/observatory-integration.md @@ -1,24 +1,12 @@ # Observatory evidence integration -`tools.tls_probe.probe_openssl` collects a fresh TLS 1.3 handshake using the -operator's explicit endpoint and pinned IP address. OpenSSL performs certificate -chain and DNS/IP identity validation with `-verify_return_error`. The collector -bounds diagnostic output to 64 KiB, kills timed out subprocesses, sends no -application request and discards application stdout. A missing hybrid provider -fails rather than silently retrying with a classical configuration. +`tools.tls_probe.probe_openssl` collects a fresh TLS 1.3 handshake using the operator's explicit endpoint and pinned IP address. OpenSSL performs certificate chain and DNS/IP identity validation with `-verify_return_error`. The collector bounds diagnostic output to 64 KiB, kills timed out subprocesses, sends no application request and discards application stdout. A missing hybrid provider fails rather than silently retrying with a classical configuration. -Default offered groups are `X25519MLKEM768:X25519:secp256r1`. The reported group -must actually be negotiated. Classical fallback is reported separately; offer -only `X25519MLKEM768` to test a policy that denies classical negotiation. The -certificate/handshake signature is reported separately and never promoted to -PQC authentication. Every result covers one address, time and configuration. +Default offered groups are `X25519MLKEM768:X25519:secp256r1`. The reported group must actually be negotiated. Classical fallback is reported separately; offer only `X25519MLKEM768` to test a policy that denies classical negotiation. The certificate/handshake signature is reported separately and never promoted to PQC authentication. Every result covers one address, time and configuration. ## Authenticated ingestion -The collector returns `evidence` and `transcript`. A separately trusted issuer -places the unmodified evidence object in the `observation` claim of the compact -JWS/JWT profile in [signed-observations.md](signed-observations.md). Evidence -contains: +The collector returns `evidence` and `transcript`. A separately trusted issuer places the unmodified evidence object in the `observation` claim of the compact JWS/JWT profile in [signed-observations.md](signed-observations.md). Evidence contains: - `profile: openssl-tls13-v1`, `observedAt` in UTC with whole-second precision; - endpoint `host`, `port`, and the actual pinned IP `address`; @@ -26,10 +14,7 @@ contains: `certificateValidation: pkix-hostname`, and process `exitCode`; - `transcriptSha256`, a SHA-256 digest of the exact diagnostic bytes. -The producer must collect new validated observations, sign under its deployed -issuer identity and transfer the token plus exact transcript through its chosen -transport. This repository does not distribute or discover private signing keys. -The consumer calls: +The producer must collect new validated observations, sign under its deployed issuer identity and transfer the token plus exact transcript through its chosen transport. This repository does not distribute or discover private signing keys. The consumer calls: ```python result = ingest_measurement( @@ -38,34 +23,13 @@ result = ingest_measurement( ) ``` -The consumer requires an explicitly trusted issuer, key and subject/audience/ -purpose policy, valid signature, expiry, replay protection, fresh measurement, -exact endpoint/subject binding, transcript digest, successful validation and a -negotiated group from the offered list. A cryptographically valid token is -consumed even when its measurement is subsequently rejected; a correction needs -a new token identifier. `validUntil` is the earliest of token expiry, issuer -maximum age and measurement maximum age; cached reports must recheck it. A signed observation is an authenticated collector -statement, **not independent verification** of that collector's behavior. +The consumer requires an explicitly trusted issuer, key and subject/audience/ purpose policy, valid signature, expiry, replay protection, fresh measurement, exact endpoint/subject binding, transcript digest, successful validation and a negotiated group from the offered list. A cryptographically valid token is consumed even when its measurement is subsequently rejected; a correction needs a new token identifier. `validUntil` is the earliest of token expiry, issuer maximum age and measurement maximum age; cached reports must recheck it. A signed observation is an authenticated collector statement, **not independent verification** of that collector's behavior. -## Existing Observatory boundary +## Deployment and assurance boundary -The inspected Observatory baseline did not enforce a validated TLS 1.3 handshake -or retain all required evidence. Its historical database rows and public aggregates -cannot satisfy this authenticated profile. No unsigned legacy row is upgraded to -trusted evidence. qScan support hints also do not substitute for a validated -completed handshake. +The production collector supports new bounded, certificate-validated evidence and export through an existing authority. The reference issuer profile is RFC 9964 ML-DSA-65 JWS with explicit issuer, audience, subject and exact manifest snapshot binding. The persistent consumer requires that exact binding. Unsigned database rows, public aggregates and qScan support hints cannot satisfy this authenticated profile. -Companion Observatory changes collect new bounded, certificate-validated evidence -and support export through the existing authority. The authority issues standard -RFC 9964 ML-DSA-65 JWS using the existing online key, with explicitly configured -issuer, audience, subject and optional exact snapshot bindings. Local cross-repository -tests exercise actual Observatory collection, authority issuance, ingestion and -monitor regression handling. Those tests use isolated test keys and local endpoints. - -The running service's version, runtime identity, deployed issuer policy and fresh -production output must still be verified. A configured GitHub KMS identity does not -establish that the VM has that identity. Existing production keys are reused; no -private signing-service source or credentials are included in this repository. +Local integration tests exercise actual collection, issuance, ingestion and monitor regression handling with isolated keys and endpoints. [Production acceptance](implementation-status.md) separately records the deployed own-origin path and its limits. The producer's identity, consumer trust, public artifact delivery and persistent monitoring each need their own evidence; access to signing in one runtime does not establish access in another. No private signing-service source or credentials are distributed here. ## Reproduce validation @@ -75,7 +39,4 @@ OPENSSL_BINARY=/path/to/openssl-3.5/bin/openssl REQUIRE_HYBRID_TLS=1 \ python -m unittest discover -s tests -p 'test_observatory_ingestion.py' -v ``` -The live test starts local TLS servers and exercises actual hybrid negotiation, -classical fallback, a hybrid-only refusal, hostname mismatch, untrusted -certificates and TLS 1.2 rejection. The required hybrid gate fails if the runtime -cannot negotiate the group. Synthetic transcript tests are not a substitute. +The live test starts local TLS servers and exercises actual hybrid negotiation, classical fallback, a hybrid-only refusal, hostname mismatch, untrusted certificates and TLS 1.2 rejection. The required hybrid gate fails if the runtime cannot negotiate the group. Synthetic transcript tests are not a substitute. diff --git a/docs/qscan-integration.md b/docs/qscan-integration.md index 8965aa9..1cfa3a6 100644 --- a/docs/qscan-integration.md +++ b/docs/qscan-integration.md @@ -1,13 +1,8 @@ # qScan v1 to v2 bridge -The existing qScan command produces v1. This repository provides an offline bridge; -it does not silently change pqc-tools or claim that its CLI emits v2. The integration -is tested against pqc-tools commit -`6a81b5537eaedf61f259334b4e430e580c35942c` (core 0.12.0). +The existing qScan command produces v1. This repository provides an offline bridge; it does not silently change pqc-tools or claim that its CLI emits v2. The integration is tested against pqc-tools commit `6a81b5537eaedf61f259334b4e430e580c35942c` (core 0.12.0). -Generate a source manifest with qScan, setting `GITHUB_REPOSITORY` and `GITHUB_SHA` -to the scanned repository and commit. Keep its exact bytes in `qscan-v1.json`. -Supply operator-declared profiles as a JSON array and run: +Generate a source manifest with qScan, setting `GITHUB_REPOSITORY` and `GITHUB_SHA` to the scanned repository and commit. Keep its exact bytes in `qscan-v1.json`. Supply operator-declared profiles as a JSON array and run: ```sh python -m tools.qscan qscan-v1.json \ @@ -16,33 +11,15 @@ python -m tools.qscan qscan-v1.json \ > crypto-agility-v2.json ``` -Repository and commit must match the source. The bridge copies source generation -time instead of relabeling an old scan as fresh, uses an explicit expiry, checks -both schemas, and emits deterministic bytes for fixed inputs. SHA-256 binds the -complete original input bytes, including tool version, counts, policy and optional -attestation URL. Store the original beside the v2 manifest for local verification. -The digest supplies integrity when compared against an independently trusted -reference; it is not a signature or scanner authenticity claim. - -Algorithm findings become `inferred` source-use observations. The operator's hybrid -flag remains `declared`, with null mapped to unknown. Absence of a finding never -becomes evidence that a deployment is safe. Profiles are supplied explicitly: -source scanning cannot identify deployed browsers, devices or TLS authentication. -Readiness scores, counts and old policy timelines remain available in the original -evidence, without promoting them to a universal security metric or fresh policy. -Legacy attestation URLs are neither followed nor promoted to trusted credentials. - -For the integration gate, check out the pinned pqc-tools revision, install/build its -packages, then run the following from this repository: +Repository and commit must match the source. The bridge copies source generation time instead of relabeling an old scan as fresh, uses an explicit expiry, checks both schemas, and emits deterministic bytes for fixed inputs. SHA-256 binds the complete original input bytes, including tool version, counts, policy and optional attestation URL. Store the original beside the v2 manifest for local verification. The digest supplies integrity when compared against an independently trusted reference; it is not a signature or scanner authenticity claim. + +Algorithm findings become `inferred` source-use observations. The operator's hybrid flag remains `declared`, with null mapped to unknown. Absence of a finding never becomes evidence that a deployment is safe. Profiles are supplied explicitly: source scanning cannot identify deployed browsers, devices or TLS authentication. Readiness scores, counts and old policy timelines remain available in the original evidence, without promoting them to a universal security metric or fresh policy. Legacy attestation URLs are neither followed nor promoted to trusted credentials. + +For the integration gate, check out the pinned pqc-tools revision, install/build its packages, then run the following from this repository: ```sh QSCAN_ROOT=/path/to/pqc-tools REQUIRE_QSCAN_INTEROP=1 \ python -m unittest discover -s tests -p 'test_qscan.py' -v ``` -The gate asserts the producer checkout commit, runs the actual source scanner on an -RSA fixture, calls the actual qScan manifest producer twice with fixed generation -inputs, checks byte reproducibility, and validates both conversions. Built JavaScript -is preferred; Node 24 can also execute the unchanged TypeScript sources without -network dependencies. The committed fixture is generated by this same producer. -This is producer/consumer interoperability, not an independent cryptographic audit. +The gate asserts the producer checkout commit, runs the actual source scanner on an RSA fixture, calls the actual qScan manifest producer twice with fixed generation inputs, checks byte reproducibility, and validates both conversions. Built JavaScript is preferred; Node 24 can also execute the unchanged TypeScript sources without network dependencies. The committed fixture is generated by this same producer. This is producer/consumer interoperability, not an independent cryptographic audit. diff --git a/docs/runtime-evidence.md b/docs/runtime-evidence.md index f07969c..ed54d74 100644 --- a/docs/runtime-evidence.md +++ b/docs/runtime-evidence.md @@ -1,17 +1,7 @@ # Runtime evidence -A declared profile says what a publisher claims. A transcript records an observation; -its bytes alone do not authenticate its source. The active OpenSSL collector checks -TLS 1.3 negotiation, the selected NamedGroup, PKIX certificate validation and hostname -validation. Classical fallback is observed separately. A hybrid exchange does not -make a classical certificate or peer signature post-quantum secure. +A declared profile says what a publisher claims. A transcript records an observation; its bytes alone do not authenticate its source. The active OpenSSL collector checks TLS 1.3 negotiation, the selected NamedGroup, PKIX certificate validation and hostname validation. Classical fallback is observed separately. A hybrid exchange does not make a classical certificate or peer signature post-quantum secure. -`tools.observatory_ingestion` accepts a bounded transcript only when its compact JWS -validates against an operator-provisioned issuer policy and binds the exact endpoint, -probe parameters, observation time and transcript digest. The monitor additionally -requires the signed manifest digest. See `signed-observations.md` for claims, -replay, expiry and trust configuration. An authenticated collector statement remains -`independentlyVerified: false`; a relying party needs its own fresh probe to establish -an independent observation. +`tools.observatory_ingestion` accepts a bounded transcript only when its compact JWS validates against an operator-provisioned issuer policy and binds the exact endpoint, probe parameters, observation time and transcript digest. The monitor additionally requires the signed manifest digest. See `signed-observations.md` for claims, replay, expiry and trust configuration. An authenticated collector statement remains `independentlyVerified: false`; a relying party needs its own fresh probe to establish an independent observation. No supported method proves every fleet member's behavior or inventory completeness. diff --git a/docs/security-review.md b/docs/security-review.md new file mode 100644 index 0000000..e425ab1 --- /dev/null +++ b/docs/security-review.md @@ -0,0 +1,11 @@ +# Security review + +Threats considered: forged or replayed collector statements, wrong-subject evidence, stale measurements in fresh wrappers, algorithm confusion, offered-group spoofing, invalid certificates, malicious HTTP destinations, resource exhaustion, local state symlinks and concurrent writers. The reference tests exercise these boundaries. + +The implementation uses standard TLS certificate/hostname verification and compact JWS/JWT. It does not create a signature canonicalization algorithm, hybrid combiner, Merkle construction or new certificate-validation mechanism. Ed25519 statement signatures are classical; hybrid TLS key establishment says nothing by itself about post-quantum authentication. + +Declared capabilities remain declarations. Authenticated observations mean an explicitly trusted collector signed scoped evidence. Independently verified security requires corroborating measurements and an explicit relying-party decision; a publisher cannot select this status in a manifest. + +State is protected from partial writes, concurrent writers, permissive file modes, symlinks and observable rollback. It cannot defend against the account owner, root, loss of the state directory or restored disk images. An operator must retain and recover trusted checkpoints. No alert delivery is claimed without a delivery receipt. + +Operational acceptance and continuing assurance limits are recorded in [implementation-status.md](implementation-status.md). Source-level security review and passing adversarial tests are not a cryptographic certification or an IETF review. diff --git a/docs/signed-observations.md b/docs/signed-observations.md index 7e07d11..e7ec36c 100644 --- a/docs/signed-observations.md +++ b/docs/signed-observations.md @@ -1,36 +1,16 @@ # Signed observation profile -The reference implementation uses compact JWS (RFC 7515), JWT claims (RFC 7519), -Ed25519 JOSE signing (RFC 8037), ML-DSA-65 (RFC 9964), and PyJWT with -RFC 8725 validation safeguards. -It signs the exact encoded JWS bytes, not a custom canonical JSON representation. -Ed25519 is classical; ML-DSA-65 is post-quantum. Both authenticate the issuer's statement, -not the truth of a measurement, PQC authentication, or fleet coverage. - -This is a local reference application profile, not a registered media type or a -required Internet-Draft wire format. Other attestation systems need their own -explicitly selected, independently reviewed verifier. Unsupported formats fail -closed; existing unsigned telemetry cannot become trusted merely by importing it. +The reference implementation uses compact JWS (RFC 7515), JWT claims (RFC 7519), Ed25519 JOSE signing (RFC 8037), ML-DSA-65 (RFC 9964), and PyJWT with RFC 8725 validation safeguards. It signs the exact encoded JWS bytes, not a custom canonical JSON representation. Ed25519 is classical; ML-DSA-65 is post-quantum. Both authenticate the issuer's statement, not the truth of a measurement, PQC authentication, or fleet coverage. + +This is a local reference application profile, not a registered media type or a required Internet-Draft wire format. Other attestation systems need their own explicitly selected, independently reviewed verifier. Unsupported formats fail closed; existing unsigned telemetry cannot become trusted merely by importing it. ## Contract -The protected header is exactly `alg` (`EdDSA` or `ML-DSA-65`), `kid`, and -`typ: pqc-observation+jwt`. Required claims are `iss`, `sub`, `aud` (one string), -`iat`, `exp` (integer NumericDate), `jti`, `purpose: pqc-observation`, and an -`observation` object. Optional `nbf` is checked. The verifier rejects duplicate -JSON members, unsupported headers, embedded key sources, future issuance, -expired or excessively old statements, and signatures from unconfigured keys. +The protected header is exactly `alg` (`EdDSA` or `ML-DSA-65`), `kid`, and `typ: pqc-observation+jwt`. Required claims are `iss`, `sub`, `aud` (one string), `iat`, `exp` (integer NumericDate), `jti`, `purpose: pqc-observation`, and an `observation` object. Optional `nbf` is checked. The verifier rejects duplicate JSON members, unsupported headers, embedded key sources, future issuance, expired or excessively old statements, and signatures from unconfigured keys. -`verify_envelope` requires a local trust configuration, expected subject, expected -audience and persistent `ReplayStore`. `expected_claims` can require additional -signed scope/policy bindings; `expected_snapshot` binds `snapshotDigest` to the -caller's independently calculated value. The monitor requires -`sha256:` over the exact received manifest bytes. No canonicalization -is implied. The measurement signs the transcript digest and endpoint/probe metadata; -see `observatory-integration.md`. +`verify_envelope` requires a local trust configuration, expected subject, expected audience and persistent `ReplayStore`. `expected_claims` can require additional signed scope/policy bindings; `expected_snapshot` binds `snapshotDigest` to the caller's independently calculated value. The monitor requires `sha256:` over the exact received manifest bytes. No canonicalization is implied. The measurement signs the transcript digest and endpoint/probe metadata; see `observatory-integration.md`. -An operator provisions a configuration of this shape (the public-key placeholder -must be replaced by an independently authenticated, unpadded base64url 32-byte key): +An operator provisions a configuration of this shape (the public-key placeholder must be replaced by an independently authenticated, unpadded base64url 32-byte key): ```json { @@ -48,86 +28,38 @@ must be replaced by an independently authenticated, unpadded base64url 32-byte k } ``` -Subject/audience/purpose matching is exact, with no wildcard or substring matching. -Remove an issuer/key, set issuer `disabled: true`, or add its key ID to -`revokedKeys` to revoke trust. The verifier never obtains keys from token URLs. -The local policy is operator-controlled, not an authenticated online revocation -service. Issuer age and lifetime limits default to 3600 seconds and cannot exceed -604800 seconds. No future-issuance clock tolerance is granted. +Subject/audience/purpose matching is exact, with no wildcard or substring matching. Remove an issuer/key, set issuer `disabled: true`, or add its key ID to `revokedKeys` to revoke trust. The verifier never obtains keys from token URLs. The local policy is operator-controlled, not an authenticated online revocation service. Issuer age and lifetime limits default to 3600 seconds and cannot exceed 604800 seconds. No future-issuance clock tolerance is granted. -## Existing ML-DSA authority +## ML-DSA key and algorithm policy -For an existing AWS KMS `ML_DSA_65` key, pin the independently authenticated public -key returned by `GetPublicKey`. Each `keys[kid]` value uses this explicit shape: +For an existing AWS KMS `ML_DSA_65` key, pin the independently authenticated public key returned by `GetPublicKey`. Each `keys[kid]` value uses this explicit shape: ```json {"alg":"ML-DSA-65","format":"spki-der","publicKey":"BASE64URL_UNPADDED_TRUSTED_DER"} ``` -The DER SubjectPublicKeyInfo must match RFC 9881: OID `2.16.840.1.101.3.4.3.18`, -absent parameters, and a 1952-byte public key. Signature length is 3309 bytes. -An EdDSA string key never authorizes ML-DSA, and an explicit algorithm mismatch -fails before signature verification. Explicit EdDSA keys may use the same object -shape with `alg: EdDSA` and `format: raw`. - -[RFC 9964 Section 5](https://www.rfc-editor.org/rfc/rfc9964.html#section-5) requires -pure ML-DSA with an empty context; HashML-DSA and nonempty contexts are rejected. -KMS signs the exact ASCII JWS signing input with `SigningAlgorithm: -ML_DSA_SHAKE_256`, `MessageType: RAW`. Its input limit is 4096 bytes: a producer -using RAW must reject larger signing inputs, not silently prehash them. AWS also -defines compatible external-mu signing, but the RAW producer does not need it. -The signing input includes the protected header and payload, not the signature. - -Python cryptography 46 has no ML-DSA API. A PyJWT algorithm adapter delegates to -OpenSSL 3.5's maintained default provider via `pkeyutl -verify -rawin`, with a -5-second deadline and 4096-byte diagnostic limit. Set `OPENSSL_BINARY` locally to -the tested OpenSSL binary. Tokens cannot select binaries or providers. The -reference test build pins OpenSSL 3.5.9. Backend absence fails verification. - -Primary sources: [RFC 9964](https://www.rfc-editor.org/rfc/rfc9964.html), -[RFC 9881](https://www.rfc-editor.org/rfc/rfc9881.html), -[AWS KMS Sign](https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html), -[AWS ML-DSA/JWT integration](https://aws.amazon.com/blogs/security/how-to-create-post-quantum-signatures-using-aws-kms-and-ml-dsa/), -and [OpenSSL ML-DSA](https://docs.openssl.org/3.5/man7/EVP_SIGNATURE-ML-DSA/). +The DER SubjectPublicKeyInfo must match RFC 9881: OID `2.16.840.1.101.3.4.3.18`, absent parameters, and a 1952-byte public key. Signature length is 3309 bytes. An EdDSA string key never authorizes ML-DSA, and an explicit algorithm mismatch fails before signature verification. Explicit EdDSA keys may use the same object shape with `alg: EdDSA` and `format: raw`. + +[RFC 9964 Section 5](https://www.rfc-editor.org/rfc/rfc9964.html#section-5) requires pure ML-DSA with an empty context; HashML-DSA and nonempty contexts are rejected. KMS signs the exact ASCII JWS signing input with `SigningAlgorithm: ML_DSA_SHAKE_256`, `MessageType: RAW`. Its input limit is 4096 bytes: a producer using RAW must reject larger signing inputs, not silently prehash them. AWS also defines compatible external-mu signing, but the RAW producer does not need it. The signing input includes the protected header and payload, not the signature. + +Python cryptography 46 has no ML-DSA API. A PyJWT algorithm adapter delegates to OpenSSL 3.5's maintained default provider via `pkeyutl -verify -rawin`, with a 5-second deadline and 4096-byte diagnostic limit. Set `OPENSSL_BINARY` locally to the tested OpenSSL binary. Tokens cannot select binaries or providers. The reference test build pins OpenSSL 3.5.9. Backend absence fails verification. + +Primary sources: [RFC 9964](https://www.rfc-editor.org/rfc/rfc9964.html), [RFC 9881](https://www.rfc-editor.org/rfc/rfc9881.html), [AWS KMS Sign](https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html), [AWS ML-DSA/JWT integration](https://aws.amazon.com/blogs/security/how-to-create-post-quantum-signatures-using-aws-kms-and-ml-dsa/), and [OpenSSL ML-DSA](https://docs.openssl.org/3.5/man7/EVP_SIGNATURE-ML-DSA/). ## Replay, storage and monitoring -The replay store atomically consumes `(issuer, audience, jti)` until expiry and -pins the latest issuance time for each issuer/subject/audience/purpose. Older -issuance is rejected even with a new ID. There are at most 10000 active tokens and -10000 pinned scopes by default; scope pins do not expire. Capacity fails closed -rather than discarding anti-rollback history. Plan separate stores for bounded -fleets; deliberate rebootstrap requires a fresh trusted baseline. - -POSIX state files are owner-only, reject symbolic links/nonregular files, serialize -writers, and use unique atomic replacement with file and directory fsync. Keep -state in an owner-controlled directory. These controls cannot defend against the -account owner/root, deleted state, or a rolled-back filesystem backup. Protect -backups and do not delete replay state to recover a rejected token. - -The monitor's process-local cache reuses a previously authenticated result only -while exact token, transcript, manifest, audience, subject and trust-policy bytes -remain unchanged. Effective validity is the earliest of token expiry, issuer -maximum age, and measurement maximum age. Each check reevaluates validity. After a -restart the producer must provide a fresh token ID; replayed input is rejected. -A token whose signature passes but measurement semantics fail is consumed; a -correction requires a new ID. This prevents retry ambiguity from being treated as -new evidence. - -`python -m tools.monitor MANIFEST --state STATE --token TOKEN --transcript LOG ---trust TRUST --audience local-monitor --replay-state REPLAY --iterations 0` -repeats local checks. Set `--interval` (minimum 1 second), or use a supervisor for -one-shot checks. Replace `MANIFEST` with `--origin https://service.example ---subject https://service.example` for allowlisted HTTPS discovery with bounded -conditional caching. Discovery failure preserves the previous monitoring baseline. - -Alerts include stable IDs, severity and a concrete action; they cover new findings, removed coverage, TLS hybrid-to-classical changes and expired/lost authenticated evidence. With `--state`, pending alerts are saved atomically alongside the baseline and returned on subsequent checks until acknowledged. A failed output write or process restart therefore does not discard an undelivered alert. Each pending occurrence has a `deliveryId`; after confirmed destination delivery, run `python -m tools.monitor --state STATE --ack-alert DELIVERY_ID` (repeat `--ack-alert` for multiple receipts). A repeated acknowledgment is harmless and cannot acknowledge a later recurrence of the same stable alert ID. JSON output alone is not a delivery receipt: the supervisor must route alerts, confirm delivery, and acknowledge them. No production daemon or issuer deployment is implied. +The replay store atomically consumes `(issuer, audience, jti)` until expiry and pins the latest issuance time for each issuer/subject/audience/purpose. Older issuance is rejected even with a new ID. There are at most 10000 active tokens and 10000 pinned scopes by default; scope pins do not expire. Capacity fails closed rather than discarding anti-rollback history. Plan separate stores for bounded fleets; deliberate rebootstrap requires a fresh trusted baseline. + +POSIX state files are owner-only, reject symbolic links/nonregular files, serialize writers, and use unique atomic replacement with file and directory fsync. Keep state in an owner-controlled directory. These controls cannot defend against the account owner/root, deleted state, or a rolled-back filesystem backup. Protect backups and do not delete replay state to recover a rejected token. + +The monitor's process-local cache reuses a previously authenticated result only while exact token, transcript, manifest, audience, subject and trust-policy bytes remain unchanged. Effective validity is the earliest of token expiry, issuer maximum age, and measurement maximum age. Each check reevaluates validity. After a restart the producer must provide a fresh token ID; replayed input is rejected. A token whose signature passes but measurement semantics fail is consumed; a correction requires a new ID. This prevents retry ambiguity from being treated as new evidence. + +`python -m tools.monitor MANIFEST --state STATE --token TOKEN --transcript LOG --trust TRUST --audience local-monitor --replay-state REPLAY --iterations 0` repeats local checks. Set `--interval` (minimum 1 second), or use a supervisor for one-shot checks. Replace `MANIFEST` with `--origin https://service.example --subject https://service.example` for allowlisted HTTPS discovery with bounded conditional caching. Discovery failure preserves the previous monitoring baseline. + +Alerts include stable IDs, severity and a concrete action; they cover new findings, removed coverage, TLS hybrid-to-classical changes and expired/lost authenticated evidence. With `--state`, pending alerts are saved atomically alongside the baseline and returned on subsequent checks until acknowledged. A failed output write or process restart therefore does not discard an undelivered alert. Each pending occurrence has a `deliveryId`; after confirmed destination delivery, run `python -m tools.monitor --state STATE --ack-alert DELIVERY_ID` (repeat `--ack-alert` for multiple receipts). A repeated acknowledgment is harmless and cannot acknowledge a later recurrence of the same stable alert ID. JSON output alone is not a delivery receipt: the supervisor must route alerts, confirm delivery, and acknowledge them. The [persistent consumer and delivery adapter](consumer-deployment.md) implement this operational path; [implementation status](implementation-status.md) records observed deployment acceptance. Rejected or missing measurements remain absent from the current report. The state separately retains the last authenticated measurements as historical regression baselines, so rejected evidence cannot hide a subsequent hybrid-to-classical change. These retained measurements never establish current assurance. Pending alerts and historical baselines share the bounded state file; exceeding its size limit fails closed and requires operator recovery rather than silently discarding history. ## Interoperability boundary -Tests use independent Node `crypto` JWS issuance and Python verification, including -real local TLS evidence. Production integration still requires a compatible issuer, -authorized collector keys, collector deployment and operational monitoring. No -private signing-service source or keys are included in this repository. +Tests use independent Node `crypto` JWS issuance and Python verification, including real local TLS evidence. Every deployment requires a compatible issuer, authorized collector keys and operational monitoring; production acceptance for the deployed own-origin path is documented separately. No private signing-service source or keys are included in this repository. diff --git a/docs/superpowers/plans/2026-10-11-production-validation.md b/docs/superpowers/plans/2026-10-11-production-validation.md deleted file mode 100644 index ea363a8..0000000 --- a/docs/superpowers/plans/2026-10-11-production-validation.md +++ /dev/null @@ -1,19 +0,0 @@ -# Crypto-agility v2 production validation plan - -**Goal:** Complete the existing PR implementation and verify production acceptance using the existing authority and collector. - -**Architecture:** Retain the deployed Observatory timer and database, existing AWS signing identity and online key. Keep authenticated transport, issuer authorization, exact snapshot binding and durable state explicit. Never substitute fixture tests for observed production behavior. - -**Constraints:** No private Authority source in public repositories, no credential copying, no new authority, one squashed change per affected repository, exact committed hosted CI, no IETF submission before readiness. - -- [x] Read instructions, implementations, workflows, deployment docs and PR discussions; isolate worktrees and confirm remote heads. -- [x] Discover production service, worker path, runtime and existing signing context without exposing environment values. -- [x] Fix monitor baseline retention and durable alert outbox with regression tests and independent security review. -- [x] Disclose the proposed TLS 1.3/certificate validation methodology break without inventing a rollout date or device measurements. -- [x] Verify existing-key canary in website Actions with public-key continuity pin and no ledger mutation. -- [ ] Validate authenticated collection, exact binding, one serialized persistent ledger, consumer trust and persistent monitor/replay state before deployment acceptance. -- [ ] Establish alert destination with the operator and verify receipt; retain pending alerts until explicit acknowledgement. -- [ ] Run exact-tree hosted checks, review resulting evidence and merge only if every acceptance boundary is met. -- [ ] Recheck IETF requirements and preserve a handoff for any remaining operational or independent-review blocker. - -Review focus: crash after state persistence; rejected evidence followed by a real TLS regression; legacy writers racing the issuer; unsigned transport records substituted for authenticated collection; unpublished or unverified method change misrepresented as observed rollout. diff --git a/draft/draft-acosta-crypto-agility-manifest-01.md b/draft/draft-acosta-crypto-agility-manifest-01.md deleted file mode 100644 index 9eaede9..0000000 --- a/draft/draft-acosta-crypto-agility-manifest-01.md +++ /dev/null @@ -1,504 +0,0 @@ -# A Well-Known URI and JSON Format for Publishing Cryptographic Posture (the Crypto-Agility Manifest) - -- **Internet-Draft:** draft-acosta-crypto-agility-manifest-01 -- **Category:** Informational -- **IPR:** trust200902 -- **Submission type:** IETF (individual submission) -- **Author:** Leon Acosta, QuantaKrypto, Argentina (contact: `leon@quantakrypto.com`) -- **Date:** 2026 - -> This Markdown file is a readable rendering of the Internet-Draft so it can be -> read on GitHub without running xml2rfc. The authoritative source is -> `draft-acosta-crypto-agility-manifest-01.xml`. If the two ever disagree, the XML -> is normative. - -> **This document is a proposal.** It is not an IETF product and is not a standard -> of any kind. - -## Abstract - -This document defines a discoverable, machine-readable JSON document, the -crypto-agility manifest, that a website or source repository publishes at the -well-known URI `/.well-known/crypto-agility.json` to declare its cryptographic -posture: a readiness summary, a compact Cryptography Bill of Materials (CBOM) -summary, an optional link to a posture attestation, the migration policy it -measures itself against, and an optional self-declared conformance statement with -justified exceptions. The manifest lets an automated consumer, such as an AI -coding agent, a continuous-integration bot, or an auditor's tool, discover a -project's crypto posture the way it already discovers a security contact from -`security.txt`. The manifest is a public, self-reported claim; it is not a proof. -It is intended to complement, not replace, a full CBOM inventory, serving as the -CBOM's public-facing discovery counterpart. - -## 1. Introduction - -Crypto-agility, the capability to replace and adapt cryptographic algorithms while -preserving security and operations, is a mature and well-documented objective. It is -treated at the algorithm-agility level by [RFC7696], and at the organizational and -lifecycle level by [NIST-CSWP-39]. Mechanism-level guidance for deriving keys when a -classical and a post-quantum exchange are combined appears in [NIST-SP-800-56C], and -security-evaluation criteria for quantum key distribution in [ISO-IEC-23837]. The -migration timelines that give crypto-agility -its present urgency are set out in [NSM-10] and [NIST-IR-8547], and general -engineering guidance for the post-quantum transition itself is collected in -[RFC9958]. The inventory of what -cryptography a system actually uses now has a standard representation, the -Cryptography Bill of Materials (CBOM) defined in [CycloneDX-CBOM]. - -What is missing is not the concept and not the inventory format. It is a -conventional, discoverable place for a project to publish a short, public summary of -its posture that a machine can find and read without out-of-band configuration. -Automated agents increasingly make dependency and cryptography choices, and -continuous-integration systems increasingly gate on them, but there is no standard -location from which such a consumer can learn a project's crypto posture, policy, and -migration deadlines. Today every such integration is bespoke. - -This document fills exactly that gap and no more. It borrows its structural model -directly from `security.txt` [RFC9116]: a small file at a registered well-known -location [RFC8615] that any party can fetch from an origin alone. Where `security.txt` -publishes how to report a vulnerability, the crypto-agility manifest publishes a -project's cryptographic posture. A full CBOM answers "what cryptography is in here"; -the manifest is its public-facing discovery counterpart, answering "where do I find -this project's posture, and what does it summarize to". - -The manifest is deliberately a summary and not a replacement for a full CBOM or a -detailed readiness report. It is small enough to fetch and parse on every CI run, and -it answers three questions at a glance: how ready is this project, what -quantum-vulnerable cryptography is it still using, and by when has it committed to -migrate. A consumer that needs component-level detail follows the CBOM pointer the -manifest carries. - -### 1.1. Scope and Non-Goals - -This document specifies the well-known URI, the media type, and the JSON field -definitions of the manifest. It does not define a new cryptographic mechanism, a new -attestation format, or a new CBOM format; it references the CBOM format of -[CycloneDX-CBOM] for the summary it carries. It does not specify how a producer -computes a readiness score, and it does not define the trust model of the credential -an `attestation` link points to. The manifest is a self-reported claim (see -Section 7); establishing whether that claim is true is out of scope and is the job of -the linked attestation and of the consumer. - -## 2. Terminology and Conventions - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD -NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to -be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they -appear in all capitals, as shown here. - -The following terms are used in this document: - -- **manifest:** the JSON document defined by this specification. -- **producer:** the party or tool that generates and publishes a manifest. -- **consumer:** any party that retrieves and interprets a manifest, for example an - agent, a CI system, a scanner, or an auditor's tool. -- **origin:** as defined in [RFC9110], the scheme, host, and port from which the - manifest is retrieved. -- **CBOM:** a Cryptography Bill of Materials as defined in [CycloneDX-CBOM]. -- **classical public-key cryptography:** public-key algorithms, such as RSA and - elliptic-curve Diffie-Hellman, whose security is broken by a cryptographically - relevant quantum computer. - -Field names, JSON literals, and example values are shown in a monospaced font. JSON -types are as defined by the JSON grammar: object, array, string, number, boolean, and -null. In this document, "integer" denotes a JSON number with no fractional part. - -## 3. The Well-Known URI - -A producer publishes its manifest at the path `/.well-known/crypto-agility.json` of an -origin, that is: - -``` -https://example.com/.well-known/crypto-agility.json -``` - -This location uses the well-known URI mechanism of [RFC8615], the same mechanism that -hosts `security.txt` [RFC9116]. The URI suffix `crypto-agility.json` is registered in -Section 9. A consumer that knows only an origin MUST be able to locate the manifest by -resolving this path against that origin, with no out-of-band configuration. - -A manifest published for a website or service SHOULD be served from the origin's web -root at the path above. A manifest that describes a source repository MAY additionally -be committed into the repository at `/.well-known/crypto-agility.json` so that it -travels with the source and can be regenerated by continuous integration. - -A manifest retrieved over HTTP or HTTPS SHOULD be retrieved over HTTPS (see -Section 8). A consumer MUST NOT follow a cross-origin redirect to locate a manifest -without treating the result as belonging to the redirect target rather than the -original origin. - -## 4. File Format - -The manifest is a single JSON [RFC8259] object encoded in UTF-8. When served over -HTTP, it SHOULD be served with a `Content-Type` header field [RFC9110] of -`application/json`. A consumer MUST reject a document that is not a single JSON object. - -The top-level `version` field is the schema contract. A consumer MUST branch on -`version` and MUST reject a value it does not understand. This document defines -`version` 1. Additive, backward-compatible fields MAY be introduced under `version` 1; -any breaking change bumps `version`. A consumer MUST ignore object members it does not -recognize, so that additive fields do not break older consumers. - -The manifest is public by design. It MUST NOT contain secrets, source snippets, file -contents, or any data not intended for public disclosure (see Section 8). - -## 5. Field Definitions - -The manifest object has the members defined in this section. Unless a member is -explicitly marked optional, it is REQUIRED and a conforming producer MUST emit it. A -machine-readable JSON Schema for `version` 1 accompanies this specification -(`schema/crypto-agility.schema.json`). - -### 5.1. version - -An integer. The manifest schema version. For this specification the value MUST be 1. A -consumer MUST reject a version it does not understand. - -### 5.2. manifestType - -A string. A discriminator that lets a consumer distinguish this document from other -JSON served at a well-known path. The value MUST be the string `crypto-agility`. A -consumer MUST reject a document whose `manifestType` is any other value. - -### 5.3. generatedAt - -A string. The instant the manifest was generated, formatted as an Internet timestamp -per [RFC3339] (a profile of ISO 8601). A producer MUST set this from its runtime clock -at generation time. - -### 5.4. generator - -An object describing what produced the manifest. It has two REQUIRED string members: - -- **name:** the name of the producing tool. -- **version:** the version of the producing tool. - -### 5.5. subject - -An object identifying what the manifest describes. It has three REQUIRED members: - -- **root:** a string. The scanned root the manifest describes. -- **repository:** a string or null. The source repository the manifest describes. - MUST be null when unknown. -- **commit:** a string or null. The commit identifier the manifest was derived from. - MUST be null when unknown. - -### 5.6. posture - -An object carrying the distilled cryptographic posture. It has four REQUIRED members: - -- **readinessScore:** a number from 0 to 100 inclusive. A producer-defined readiness - score. A value of 100 means no classical asymmetric cryptography was found. This - document does not define how the score is computed; a consumer MUST NOT assume that - scores from different producers are directly comparable. -- **hybridKexInUse:** a boolean or null. Whether the subject negotiates hybrid - post-quantum key exchange. A producer that cannot determine this (for example a - static source scan, which cannot observe a negotiated key-exchange group) MUST set - null. A consumer MUST treat null as "unknown", distinct from false. -- **quantumVulnerable:** an object with a REQUIRED integer member `total` (the count - of quantum-vulnerable findings) and a REQUIRED object member `bySeverity`. - `bySeverity` MUST contain all five integer keys `critical`, `high`, `medium`, `low`, - and `info`, each a count that MUST NOT be negative. -- **hndlExposedCount:** an integer. The number of findings exposed to - harvest-now-decrypt-later, that is, data that can be recorded today and decrypted - once a cryptographically relevant quantum computer exists. - -### 5.7. cbomSummary - -An object that summarizes, and points to, the full CBOM [CycloneDX-CBOM] from which it -was derived. The full CBOM is intentionally not inlined, so that the manifest stays -small. It has three REQUIRED members: - -- **serialNumber:** a string. The CycloneDX `serialNumber` (a `urn:uuid:` value) of - the full CBOM this summary was derived from. This links the manifest to a specific - CBOM instance. -- **assetCount:** an integer. The number of distinct `cryptographic-asset` components - in the full CBOM. -- **algorithmFamilies:** an array of objects, ordered most-referenced first. Each - element has a REQUIRED string `family` (the algorithm family name, for example `RSA` - or `ECDH`), a REQUIRED integer `count` (the number of references to that family), - and a REQUIRED boolean `quantumVulnerable`. - -### 5.8. attestation (optional) - -An OPTIONAL object. When present, it has one REQUIRED string member: - -- **url:** a URI locating a machine-verifiable posture credential. A producer MUST - record the URI verbatim and MUST NOT be required to fetch it when generating the - manifest. A producer MUST omit the `attestation` member entirely when no credential - is declared, rather than emitting an empty or null value. - -The presence of an `attestation` link does not by itself make the manifest verifiable; -it points a consumer to where verification can be performed. See Section 7. - -### 5.9. policy - -An object stating the migration policy the subject declares it is measured against. It -has five REQUIRED members: - -- **source:** a string. A label for the policy source: a published standard's name, or - the literal `operator-declared` when the deadlines are set by the operator rather - than adopted from a standard. -- **deprecateClassicalAfter:** an integer year after which classical public-key - cryptography is considered deprecated under the declared policy. -- **disallowClassicalAfter:** an integer year after which classical public-key - cryptography is considered disallowed under the declared policy. -- **transitionDeadline:** a string or null. An operator-declared migration deadline - (an [RFC3339] date or a year), or null when none is declared. -- **citation:** a string. A human-readable citation for the declared deadlines. - -The values of `deprecateClassicalAfter` and `disallowClassicalAfter` are the policy -the subject holds itself to; they are a declaration, not a verification. A producer -that adopts a published timeline SHOULD name it in `source` and `citation`, for -example the 2030 and 2035 milestones of [NIST-IR-8547] and [NSM-10]. -National-security profiles such as the milestones of [NSM-10] are a valid -`operator-declared` choice. - -### 5.10. conformance (optional) - -An OPTIONAL object stating the subject's self-declared conformance to the policy in -Section 5.9, together with any residual quantum-vulnerable findings it accepts. It lets -a subject that is not at a readiness score of 100 declare that its remaining classical -cryptography is known and accepted under the policy, not an oversight. When -present, it has two REQUIRED members and two OPTIONAL members: - -- **policy:** a string naming the policy the conformance is asserted against. It - SHOULD match the `source` member of the `policy` object. -- **status:** a string, one of `conformant`, `conditional`, or `nonconformant`. - `conformant` means the subject meets the declared policy outright. `conditional` - means the subject does not meet the numeric bar, but every residual quantum-vulnerable - finding is enumerated in `exceptions` with a justification. `nonconformant` means - residual findings exist without accepted exceptions. -- **statement:** an OPTIONAL string carrying a human-readable rationale for the status. -- **exceptions:** an OPTIONAL array of the residual findings the subject accepts. Each - element is an object with a REQUIRED `family` string (the algorithm family or finding - the exception covers, for example `RSA` or `X25519`), a REQUIRED `justification` - string, and an OPTIONAL `acceptedUntil` integer year (typically aligned with a policy - deadline) or null. - -A self-published `conformance` status is a claim of the same kind as every other value -in this document, not a proof (Section 8). A consumer MUST NOT treat a `conformant` or -`conditional` status as assurance unless the manifest also carries an `attestation` -(Section 5.8) that covers the asserted conformance and that the consumer has verified. A -producer that emits a `conformant` or `conditional` status SHOULD also provide an -`attestation`. - -## 6. Examples - -The following is a complete, conforming manifest for a repository that has migrated -most, but not all, of its key exchange to hybrid post-quantum key exchange, still uses -RSA and ECDH in two places, and adopts the [NIST-IR-8547] transition timeline. - -```json -{ - "version": 1, - "manifestType": "crypto-agility", - "generatedAt": "2026-07-27T04:57:12.323Z", - "generator": { "name": "qScan", "version": "0.5.0" }, - "subject": { - "root": ".", - "repository": "https://github.com/acme/widget-service", - "commit": "9f2c1ab7d3e04c5b8a6f0e21d47c9b3e5a1f8c02" - }, - "posture": { - "readinessScore": 74, - "hybridKexInUse": true, - "quantumVulnerable": { - "total": 2, - "bySeverity": { - "critical": 0, "high": 2, "medium": 0, "low": 0, "info": 0 - } - }, - "hndlExposedCount": 2 - }, - "cbomSummary": { - "serialNumber": "urn:uuid:5834348b-e27b-4f71-8752-f0497d98166f", - "assetCount": 2, - "algorithmFamilies": [ - { "family": "ECDH", "count": 1, "quantumVulnerable": true }, - { "family": "RSA", "count": 1, "quantumVulnerable": true } - ] - }, - "attestation": { "url": "https://quantakrypto.com/attest/acme" }, - "policy": { - "source": "NIST IR 8547", - "deprecateClassicalAfter": 2030, - "disallowClassicalAfter": 2035, - "transitionDeadline": null, - "citation": "https://csrc.nist.gov/pubs/ir/8547/ipd" - }, - "conformance": { - "policy": "NIST IR 8547", - "status": "conditional", - "statement": "Residual classical crypto is transition-accepted.", - "exceptions": [ - { - "family": "ECDH", - "justification": "classical half of a hybrid KEX", - "acceptedUntil": 2030 - }, - { - "family": "RSA", - "justification": "public-TLS cert chain; no PQC CA path yet", - "acceptedUntil": 2030 - } - ] - } -} -``` - -The `conformance` object above is optional. Because its `status` is `conditional`, the -`attestation` link is what lets a consumer treat it as more than a claim (Section 5.10). -A producer that cannot observe live key exchange (a static source scan) sets -`hybridKexInUse` to null and omits both `attestation` and `conformance` when neither is -declared. - -## 7. Operational Considerations - -**The manifest is a self-reported claim.** Every value in it is asserted by the -producer about itself. On its own the manifest establishes what a project says about -its posture, not what is true of it. This is the same trust posture as `security.txt`: -publishing a file does not prove the statements in it. - -The `attestation` link is the bridge from claim to evidence. It points a consumer to a -machine-verifiable posture credential that a third party can check independently of the -manifest. A consumer that needs assurance rather than a claim SHOULD follow -`attestation.url` and verify the credential there; the manifest itself SHOULD NOT be -treated as proof of the posture it reports. A manifest without an `attestation` member -is an unverified self-declaration and SHOULD be consumed as such. - -The manifest and its CBOM summary SHOULD be derived from the same underlying scan and -CBOM, so that the summary can never contradict the full CBOM referenced by -`cbomSummary.serialNumber`. A consumer that needs component-level detail follows that -pointer to the full CBOM rather than relying on the summary. - -Producers SHOULD regenerate the manifest as posture changes, for example on each CI -run, so that a stale manifest does not misrepresent a project that has since migrated -or regressed. `generatedAt` lets a consumer judge freshness and reject a manifest that -is older than a policy it enforces. - -**Relationship to security.txt.** An origin that already publishes a `security.txt` -file [RFC9116] SHOULD co-locate this manifest beside it under `/.well-known/`, so that -an operator who maintains one maintains the other and the two describe a consistent -security posture. RFC 9116 does not define a field for pointing at an arbitrary -well-known resource, and its `Encryption` field is reserved for a key or a certificate -used to encrypt a report to the operator, not for a posture document; a producer MUST -NOT repurpose `Encryption` to reference this manifest. Where an operator wishes to make -the link explicit, they MAY add a comment line to `security.txt` naming the manifest's -well-known location. A consumer that has discovered a `security.txt` file SHOULD attempt -to resolve this manifest at its own well-known path independently, rather than relying -on any such reference. - -## 8. Security Considerations - -**A manifest is a claim, not a proof.** Nothing about serving a manifest establishes -that its contents are accurate. A consumer MUST NOT treat a manifest as an -authenticated statement of posture; a consumer that requires assurance MUST obtain it -from a verifiable credential (see Section 7), not from the manifest alone. A hostile or -careless producer can overstate readiness, understate vulnerable findings, or advertise -deadlines it does not meet. A consumer that gates a decision on an unverified manifest -inherits that risk. - -**Self-declared conformance is not a pass.** The optional `conformance` object -(Section 5.10) lets a producer assert a `conformant` or `conditional` status and -enumerate accepted exceptions. This is a producer's own assertion and carries no more -weight than the readiness score itself. A consumer MUST NOT treat a self-published -status as a pass, an approval, or evidence of accepted risk unless the manifest also -carries an `attestation` (Section 5.8) that covers the asserted conformance and that the -consumer has independently verified. Absent a verified attestation, the `status` and -`exceptions` are informative context for a human reviewer, not an authorization to -proceed. - -**Do not leak internal detail.** The manifest is public. A producer MUST NOT place -secrets, key material, source snippets, file paths, internal hostnames, or any -non-public detail in it. The fields defined here are deliberately coarse summaries for -this reason. A precise public inventory of which quantum-vulnerable algorithms a system -still uses, and where it is exposed to harvest-now-decrypt-later, is also a roadmap for -an attacker; a producer SHOULD weigh the transparency benefit of a detailed posture -against the reconnaissance value it hands an adversary, and MAY publish only the coarse -summary this document defines. - -**Retrieve over TLS.** A manifest retrieved over the network SHOULD be retrieved over -HTTPS, so that a network attacker cannot substitute or tamper with it in transit. A -consumer SHOULD reject, or clearly mark as untrusted, a manifest retrieved over an -unauthenticated channel. As with `security.txt` [RFC9116], a cross-origin redirect -encountered while retrieving a manifest MUST NOT be treated as authoritative for the -original origin. - -**Freshness and denial of service.** A stale manifest can misrepresent current -posture; consumers SHOULD use `generatedAt` to bound acceptable age. Because the -manifest is fetched automatically and potentially often, producers SHOULD serve it as a -static resource, and consumers SHOULD bound the size they will read and cache results, -so that manifest retrieval is not turned into a denial-of-service vector against either -party. - -## 9. IANA Considerations - -IANA is requested to register the following well-known URI in the "Well-Known URIs" -registry defined by [RFC8615]. - -- **URI suffix:** crypto-agility.json -- **Change controller:** IETF -- **Specification document(s):** This document. -- **Status:** permanent -- **Related information:** The resource is a JSON [RFC8259] document with media type - `application/json`. See Section 4. - -This document registers no new media type; the manifest uses the existing -`application/json` media type. - -## 10. Normative References - -- **[RFC2119]** Bradner, S., "Key words for use in RFCs to Indicate Requirement - Levels", BCP 14, RFC 2119, March 1997, . -- **[RFC8174]** Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key - Words", BCP 14, RFC 8174, May 2017, . -- **[RFC8259]** Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange - Format", STD 90, RFC 8259, December 2017, - . -- **[RFC3339]** Klyne, G. and C. Newman, "Date and Time on the Internet: Timestamps", - RFC 3339, July 2002, . -- **[RFC8615]** Nottingham, M., "Well-Known Uniform Resource Identifiers (URIs)", - RFC 8615, May 2019, . -- **[RFC9110]** Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP - Semantics", STD 97, RFC 9110, June 2022, - . -- **[CycloneDX-CBOM]** OWASP CycloneDX / Ecma International TC54, "CycloneDX - Cryptography Bill of Materials (CBOM)", 2025. Cryptographic assets in the CycloneDX - Bill of Materials specification (integrated in CycloneDX 1.6, extended in 1.7; - published as Ecma International standard ECMA-424), - . - -## 11. Informative References - -- **[RFC9116]** Foudil, E. and Y. Shafranovich, "A File Format to Aid in Security - Vulnerability Disclosure", RFC 9116, April 2022, - . -- **[RFC9958]** Banerjee, A., Reddy.K, T., Schoinianakis, D., Hollebeek, T., and M. - Ounsworth, "Post-Quantum Cryptography for Engineers", RFC 9958, June 2026, - . -- **[RFC7696]** Housley, R., "Guidelines for Cryptographic Algorithm Agility and - Selecting Mandatory-to-Implement Algorithms", BCP 201, RFC 7696, November 2015, - . -- **[NIST-CSWP-39]** National Institute of Standards and Technology, "Considerations - for Achieving Cryptographic Agility: Strategies and Practices", NIST CSWP 39, - December 2025, . -- **[NIST-SP-800-56C]** Barker, E., Chen, L., and R. Davis, "Recommendation for - Key-Derivation Methods in Key-Establishment Schemes", NIST SP 800-56C Rev. 2, - August 2020, . -- **[NIST-IR-8547]** National Institute of Standards and Technology, "Transition to - Post-Quantum Cryptography Standards", NIST IR 8547 (Initial Public Draft), - November 2024, . -- **[NSM-10]** The White House, "National Security Memorandum on Promoting United - States Leadership in Quantum Computing While Mitigating Risks to Vulnerable - Cryptographic Systems (NSM-10)", May 2022, - . -- **[ISO-IEC-23837]** ISO/IEC, "Information security - Security requirements, test and - evaluation methods for quantum key distribution - Part 1: Requirements; Part 2: - Evaluation and testing methods", ISO/IEC 23837-1:2023 and 23837-2:2023, 2023, - . - -## Appendix A. Status of This Proposal - -This document is an individual Internet-Draft, the first step of the IETF process, published to solicit review. It is not the product of an IETF working group and does not represent IETF consensus, and it is not yet a standard: it is a proposal put forward to be reviewed and, if it has merit, advanced. The reference implementation, a schema, and examples are maintained in the -open at the repository from which this draft originates. diff --git a/draft/draft-acosta-crypto-agility-manifest-01.txt b/draft/draft-acosta-crypto-agility-manifest-01.txt deleted file mode 100644 index 02109ab..0000000 --- a/draft/draft-acosta-crypto-agility-manifest-01.txt +++ /dev/null @@ -1,896 +0,0 @@ - - - - -Network Working Group L. Acosta -Internet-Draft QuantaKrypto -Intended status: Informational 7 August 2026 -Expires: 8 February 2027 - - - A Well-Known URI and JSON Format for Publishing Cryptographic Posture - (the Crypto-Agility Manifest) - draft-acosta-crypto-agility-manifest-01 - -Abstract - - This document defines a discoverable, machine-readable JSON document, - the crypto-agility manifest, that a website or source repository - publishes at the well-known URI "/.well-known/crypto-agility.json" to - declare its cryptographic posture: a readiness summary, a compact - Cryptography Bill of Materials (CBOM) summary, an optional link to a - posture attestation, the migration policy it measures itself against, - and an optional self-declared conformance statement with justified - exceptions. The manifest lets an automated consumer, such as an AI - coding agent, a continuous-integration bot, or an auditor's tool, - discover a project's crypto posture the way it already discovers a - security contact from "security.txt". The manifest is a public, - self-reported claim; it is not a proof. It is intended to - complement, not replace, a full CBOM inventory, serving as the CBOM's - public-facing discovery counterpart. - - This document is a proposal. It is not an IETF product and is not a - standard of any kind. - -Status of This Memo - - This Internet-Draft is submitted in full conformance with the - provisions of BCP 78 and BCP 79. - - Internet-Drafts are working documents of the Internet Engineering - Task Force (IETF). Note that other groups may also distribute - working documents as Internet-Drafts. The list of current Internet- - Drafts is at https://datatracker.ietf.org/drafts/current/. - - Internet-Drafts are draft documents valid for a maximum of six months - and may be updated, replaced, or obsoleted by other documents at any - time. It is inappropriate to use Internet-Drafts as reference - material or to cite them other than as "work in progress." - - This Internet-Draft will expire on 8 February 2027. - - - - - -Acosta Expires 8 February 2027 [Page 1] - -Internet-Draft Crypto-Agility Manifest August 2026 - - -Copyright Notice - - Copyright (c) 2026 IETF Trust and the persons identified as the - document authors. All rights reserved. - - This document is subject to BCP 78 and the IETF Trust's Legal - Provisions Relating to IETF Documents (https://trustee.ietf.org/ - license-info) in effect on the date of publication of this document. - Please review these documents carefully, as they describe your rights - and restrictions with respect to this document. Code Components - extracted from this document must include Revised BSD License text as - described in Section 4.e of the Trust Legal Provisions and are - provided without warranty as described in the Revised BSD License. - -Table of Contents - - 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 - 1.1. Scope and Non-Goals . . . . . . . . . . . . . . . . . . . 4 - 2. Terminology and Conventions . . . . . . . . . . . . . . . . . 4 - 3. The Well-Known URI . . . . . . . . . . . . . . . . . . . . . 5 - 4. File Format . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 5. Field Definitions . . . . . . . . . . . . . . . . . . . . . . 6 - 5.1. version . . . . . . . . . . . . . . . . . . . . . . . . . 6 - 5.2. manifestType . . . . . . . . . . . . . . . . . . . . . . 6 - 5.3. generatedAt . . . . . . . . . . . . . . . . . . . . . . . 6 - 5.4. generator . . . . . . . . . . . . . . . . . . . . . . . . 6 - 5.5. subject . . . . . . . . . . . . . . . . . . . . . . . . . 6 - 5.6. posture . . . . . . . . . . . . . . . . . . . . . . . . . 7 - 5.7. cbomSummary . . . . . . . . . . . . . . . . . . . . . . . 7 - 5.8. attestation (optional) . . . . . . . . . . . . . . . . . 8 - 5.9. policy . . . . . . . . . . . . . . . . . . . . . . . . . 8 - 5.10. conformance (optional) . . . . . . . . . . . . . . . . . 9 - 6. Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 9 - 7. Operational Considerations . . . . . . . . . . . . . . . . . 11 - 8. Security Considerations . . . . . . . . . . . . . . . . . . . 12 - 9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 13 - 10. Normative References . . . . . . . . . . . . . . . . . . . . 13 - 11. Informative References . . . . . . . . . . . . . . . . . . . 14 - Appendix A. Status of This Proposal . . . . . . . . . . . . . . 15 - Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 15 - - - - - - - - - - - -Acosta Expires 8 February 2027 [Page 2] - -Internet-Draft Crypto-Agility Manifest August 2026 - - -1. Introduction - - Crypto-agility, the capability to replace and adapt cryptographic - algorithms while preserving security and operations, is a mature and - well-documented objective. It is treated at the algorithm-agility - level by [RFC7696], and at the organizational and lifecycle level by - [NIST-CSWP-39]. Mechanism-level guidance for deriving keys when a - classical and a post-quantum exchange are combined appears in - [NIST-SP-800-56C], and security-evaluation criteria for quantum key - distribution in [ISO-IEC-23837]. The migration timelines that give - crypto-agility its present urgency are set out in [NSM-10] and - [NIST-IR-8547], and general engineering guidance for the post-quantum - transition itself is collected in [RFC9958]. The inventory of what - cryptography a system actually uses now has a standard - representation, the Cryptography Bill of Materials (CBOM) defined in - [CycloneDX-CBOM]. - - What is missing is not the concept and not the inventory format. It - is a conventional, discoverable place for a project to publish a - short, public summary of its posture that a machine can find and read - without out-of-band configuration. Automated agents increasingly - make dependency and cryptography choices, and continuous-integration - systems increasingly gate on them, but there is no standard location - from which such a consumer can learn a project's crypto posture, - policy, and migration deadlines. Today every such integration is - bespoke. - - This document fills exactly that gap and no more. It borrows its - structural model directly from "security.txt" [RFC9116]: a small file - at a registered well-known location [RFC8615] that any party can - fetch from an origin alone. Where "security.txt" publishes how to - report a vulnerability, the crypto-agility manifest publishes a - project's cryptographic posture. A full CBOM answers "what - cryptography is in here"; the manifest is its public-facing discovery - counterpart, answering "where do I find this project's posture, and - what does it summarize to". - - The manifest is deliberately a summary and not a replacement for a - full CBOM or a detailed readiness report. It is small enough to - fetch and parse on every CI run, and it answers three questions at a - glance: how ready is this project, what quantum-vulnerable - cryptography is it still using, and by when has it committed to - migrate. A consumer that needs component-level detail follows the - CBOM pointer the manifest carries. - - - - - - - -Acosta Expires 8 February 2027 [Page 3] - -Internet-Draft Crypto-Agility Manifest August 2026 - - -1.1. Scope and Non-Goals - - This document specifies the well-known URI, the media type, and the - JSON field definitions of the manifest. It does not define a new - cryptographic mechanism, a new attestation format, or a new CBOM - format; it references the CBOM format of [CycloneDX-CBOM] for the - summary it carries. It does not specify how a producer computes a - readiness score, and it does not define the trust model of the - credential an "attestation" link points to. The manifest is a self- - reported claim (see Section 7); establishing whether that claim is - true is out of scope and is the job of the linked attestation and of - the consumer. - -2. Terminology and Conventions - - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", - "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and - "OPTIONAL" in this document are to be interpreted as described in BCP - 14 [RFC2119] [RFC8174] when, and only when, they appear in all - capitals, as shown here. - - The following terms are used in this document: - - manifest: the JSON document defined by this specification. - - producer: the party or tool that generates and publishes a manifest. - - consumer: any party that retrieves and interprets a manifest, for - example an agent, a CI system, a scanner, or an auditor's tool. - - origin: as defined in [RFC9110], the scheme, host, and port from - which the manifest is retrieved. - - CBOM: a Cryptography Bill of Materials as defined in - [CycloneDX-CBOM]. - - classical public-key cryptography: public-key algorithms, such as - RSA and elliptic-curve Diffie-Hellman, whose security is broken by - a cryptographically relevant quantum computer. - - Field names, JSON literals, and example values are shown in a - monospaced font. JSON types are as defined by the JSON grammar: - object, array, string, number, boolean, and null. In this document, - "integer" denotes a JSON number with no fractional part. - - - - - - - -Acosta Expires 8 February 2027 [Page 4] - -Internet-Draft Crypto-Agility Manifest August 2026 - - -3. The Well-Known URI - - A producer publishes its manifest at the path "/.well-known/crypto- - agility.json" of an origin, that is: - - https://example.com/.well-known/crypto-agility.json - - This location uses the well-known URI mechanism of [RFC8615], the - same mechanism that hosts "security.txt" [RFC9116]. Registration of - the URI suffix "crypto-agility.json" is requested in Section 9. A - consumer that knows only an origin MUST be able to locate the - manifest by resolving this path against that origin, with no out-of- - band configuration. - - A manifest published for a website or service SHOULD be served from - the origin's web root at the path above. A manifest that describes a - source repository MAY additionally be committed into the repository - at "/.well-known/crypto-agility.json" so that it travels with the - source and can be regenerated by continuous integration. - - A manifest retrieved over HTTP or HTTPS SHOULD be retrieved over - HTTPS (see Section 8). A consumer MUST NOT follow a cross-origin - redirect to locate a manifest without treating the result as - belonging to the redirect target rather than the original origin. - -4. File Format - - The manifest is a single JSON [RFC8259] object encoded in UTF-8. - When served over HTTP, it SHOULD be served with a "Content-Type" - header field [RFC9110] of "application/json". A consumer MUST reject - a document that is not a single JSON object. - - The top-level "version" field is the schema contract. A consumer - MUST branch on "version" and MUST reject a value it does not - understand. This document defines "version" 1. Additive, backward- - compatible fields MAY be introduced under "version" 1; any breaking - change bumps "version". A consumer MUST ignore object members it - does not recognize, so that additive fields do not break older - consumers. - - The manifest is public by design. It MUST NOT contain secrets, - source snippets, file contents, or any data not intended for public - disclosure (see Section 8). - - - - - - - - -Acosta Expires 8 February 2027 [Page 5] - -Internet-Draft Crypto-Agility Manifest August 2026 - - -5. Field Definitions - - The manifest object has the members defined in this section. Unless - a member is explicitly marked optional, it is REQUIRED and a - conforming producer MUST emit it. A machine-readable JSON Schema for - "version" 1 accompanies this specification. - -5.1. version - - An integer. The manifest schema version. For this specification the - value MUST be 1. A consumer MUST reject a version it does not - understand. - -5.2. manifestType - - A string. A discriminator that lets a consumer distinguish this - document from other JSON served at a well-known path. The value MUST - be the string "crypto-agility". A consumer MUST reject a document - whose "manifestType" is any other value. - -5.3. generatedAt - - A string. The instant the manifest was generated, formatted as an - Internet timestamp per [RFC3339] (a profile of ISO 8601). A producer - MUST set this from its runtime clock at generation time. - -5.4. generator - - An object describing what produced the manifest. It has two REQUIRED - string members: - - name: the name of the producing tool. - - version: the version of the producing tool. - -5.5. subject - - An object identifying what the manifest describes. It has three - REQUIRED members: - - root: a string. The scanned root the manifest describes. - - repository: a string or null. The source repository the manifest - describes. MUST be null when unknown. - - commit: a string or null. The commit identifier the manifest was - derived from. MUST be null when unknown. - - - - -Acosta Expires 8 February 2027 [Page 6] - -Internet-Draft Crypto-Agility Manifest August 2026 - - -5.6. posture - - An object carrying the distilled cryptographic posture. It has four - REQUIRED members: - - readinessScore: a number from 0 to 100 inclusive. A producer- - defined readiness score. A value of 100 means no classical - asymmetric cryptography was found. This document does not define - how the score is computed; a consumer MUST NOT assume that scores - from different producers are directly comparable. - - hybridKexInUse: a boolean or null. Whether the subject negotiates - hybrid post-quantum key exchange. A producer that cannot - determine this (for example a static source scan, which cannot - observe a negotiated key-exchange group) MUST set null. A - consumer MUST treat null as "unknown", distinct from false. - - quantumVulnerable: an object with a REQUIRED integer member "total" - (the count of quantum-vulnerable findings) and a REQUIRED object - member "bySeverity". "bySeverity" MUST contain all five integer - keys "critical", "high", "medium", "low", and "info", each a count - that MUST NOT be negative. - - hndlExposedCount: an integer. The number of findings exposed to - harvest-now-decrypt-later, that is, data that can be recorded - today and decrypted once a cryptographically relevant quantum - computer exists. - -5.7. cbomSummary - - An object that summarizes, and points to, the full CBOM - [CycloneDX-CBOM] from which it was derived. The full CBOM is - intentionally not inlined, so that the manifest stays small. It has - three REQUIRED members: - - serialNumber: a string. The CycloneDX "serialNumber" (a "urn:uuid:" - value) of the full CBOM this summary was derived from. This links - the manifest to a specific CBOM instance. - - assetCount: an integer. The number of distinct "cryptographic- - asset" components in the full CBOM. - - algorithmFamilies: an array of objects, ordered most-referenced - first. Each element has a REQUIRED string "family" (the algorithm - family name, for example "RSA" or "ECDH"), a REQUIRED integer - "count" (the number of references to that family), and a REQUIRED - boolean "quantumVulnerable". - - - - -Acosta Expires 8 February 2027 [Page 7] - -Internet-Draft Crypto-Agility Manifest August 2026 - - -5.8. attestation (optional) - - An OPTIONAL object. When present, it has one REQUIRED string member: - - url: a URI locating a machine-verifiable posture credential. A - producer MUST record the URI verbatim and MUST NOT be required to - fetch it when generating the manifest. A producer MUST omit the - "attestation" member entirely when no credential is declared, - rather than emitting an empty or null value. - - The presence of an "attestation" link does not by itself make the - manifest verifiable; it points a consumer to where verification can - be performed. See Section 7. - -5.9. policy - - An object stating the migration policy the subject declares it is - measured against. It has five REQUIRED members: - - source: a string. A label for the policy source: a published - standard's name, or the literal "operator-declared" when the - deadlines are set by the operator rather than adopted from a - standard. - - deprecateClassicalAfter: an integer year after which classical - public-key cryptography is considered deprecated under the - declared policy. - - disallowClassicalAfter: an integer year after which classical - public-key cryptography is considered disallowed under the - declared policy. - - transitionDeadline: a string or null. An operator-declared - migration deadline (an [RFC3339] date or a year), or null when - none is declared. - - citation: a string. A human-readable citation for the declared - deadlines. - - The values of "deprecateClassicalAfter" and "disallowClassicalAfter" - are the policy the subject holds itself to; they are a declaration, - not a verification. A producer that adopts a published timeline - SHOULD name it in "source" and "citation", for example the 2030 and - 2035 milestones of [NIST-IR-8547] and [NSM-10]. National-security - profiles such as the milestones of [NSM-10] are a valid "operator- - declared" choice. - - - - - -Acosta Expires 8 February 2027 [Page 8] - -Internet-Draft Crypto-Agility Manifest August 2026 - - -5.10. conformance (optional) - - An OPTIONAL object stating the subject's self-declared conformance to - the policy in Section 5.9, together with any residual quantum- - vulnerable findings it accepts. It lets a subject that is not at a - readiness score of 100 declare that its remaining classical - cryptography is known and accepted under the policy, not an - oversight. When present, it has two REQUIRED members and two - OPTIONAL members: - - policy: a string naming the policy the conformance is asserted - against. It SHOULD match the "source" member of the "policy" - object. - - status: a string, one of "conformant", "conditional", or - "nonconformant". "conformant" means the subject meets the - declared policy outright. "conditional" means the subject does - not meet the numeric bar, but every residual quantum-vulnerable - finding is enumerated in "exceptions" with a justification. - "nonconformant" means residual findings exist without accepted - exceptions. - - statement: an OPTIONAL string carrying a human-readable rationale - for the status. - - exceptions: an OPTIONAL array of the residual findings the subject - accepts. Each element is an object with a REQUIRED "family" - string (the algorithm family or finding the exception covers, for - example "RSA" or "X25519"), a REQUIRED "justification" string, and - an OPTIONAL "acceptedUntil" integer year (typically aligned with a - policy deadline) or null. - - A self-published "conformance" status is a claim of the same kind as - every other value in this document, not a proof (Section 8). A - consumer MUST NOT treat a "conformant" or "conditional" status as - assurance unless the manifest also carries an "attestation" - (Section 5.8) that covers the asserted conformance and that the - consumer has verified. A producer that emits a "conformant" or - "conditional" status SHOULD also provide an "attestation". - -6. Examples - - The following is a complete, conforming manifest for a repository - that has migrated most, but not all, of its key exchange to hybrid - post-quantum key exchange, still uses RSA and ECDH in two places, and - adopts the [NIST-IR-8547] transition timeline. - - - - - -Acosta Expires 8 February 2027 [Page 9] - -Internet-Draft Crypto-Agility Manifest August 2026 - - - { - "version": 1, - "manifestType": "crypto-agility", - "generatedAt": "2026-07-27T04:57:12.323Z", - "generator": { "name": "qScan", "version": "0.5.0" }, - "subject": { - "root": ".", - "repository": "https://github.com/acme/widget-service", - "commit": "9f2c1ab7d3e04c5b8a6f0e21d47c9b3e5a1f8c02" - }, - "posture": { - "readinessScore": 74, - "hybridKexInUse": true, - "quantumVulnerable": { - "total": 2, - "bySeverity": { - "critical": 0, "high": 2, "medium": 0, "low": 0, "info": 0 - } - }, - "hndlExposedCount": 2 - }, - "cbomSummary": { - "serialNumber": "urn:uuid:5834348b-e27b-4f71-8752-f0497d98166f", - "assetCount": 2, - "algorithmFamilies": [ - { "family": "ECDH", "count": 1, "quantumVulnerable": true }, - { "family": "RSA", "count": 1, "quantumVulnerable": true } - ] - }, - "attestation": { "url": "https://quantakrypto.com/attest/acme" }, - "policy": { - "source": "NIST IR 8547", - "deprecateClassicalAfter": 2030, - "disallowClassicalAfter": 2035, - "transitionDeadline": null, - "citation": "https://csrc.nist.gov/pubs/ir/8547/ipd" - }, - "conformance": { - "policy": "NIST IR 8547", - "status": "conditional", - "statement": "Residual classical crypto is transition-accepted.", - "exceptions": [ - { - "family": "ECDH", - "justification": "classical half of a hybrid KEX", - "acceptedUntil": 2030 - }, - { - - - -Acosta Expires 8 February 2027 [Page 10] - -Internet-Draft Crypto-Agility Manifest August 2026 - - - "family": "RSA", - "justification": "public-TLS cert chain; no PQC CA path yet", - "acceptedUntil": 2030 - } - ] - } - } - - The "conformance" object above is optional. Because its "status" is - "conditional", the "attestation" link is what lets a consumer treat - it as more than a claim (Section 5.10). A producer that cannot - observe live key exchange (a static source scan) sets - "hybridKexInUse" to null and omits both "attestation" and - "conformance" when neither is declared. - -7. Operational Considerations - - *The manifest is a self-reported claim.* Every value in it is - asserted by the producer about itself. On its own the manifest - establishes what a project says about its posture, not what is true - of it. This is the same trust posture as "security.txt": publishing - a file does not prove the statements in it. - - The "attestation" link is the bridge from claim to evidence. It - points a consumer to a machine-verifiable posture credential that a - third party can check independently of the manifest. A consumer that - needs assurance rather than a claim SHOULD follow "attestation.url" - and verify the credential there; the manifest itself SHOULD NOT be - treated as proof of the posture it reports. A manifest without an - "attestation" member is an unverified self-declaration and SHOULD be - consumed as such. - - The manifest and its CBOM summary SHOULD be derived from the same - underlying scan and CBOM, so that the summary can never contradict - the full CBOM referenced by "cbomSummary.serialNumber". A consumer - that needs component-level detail follows that pointer to the full - CBOM rather than relying on the summary. - - Producers SHOULD regenerate the manifest as posture changes, for - example on each CI run, so that a stale manifest does not - misrepresent a project that has since migrated or regressed. - "generatedAt" lets a consumer judge freshness and reject a manifest - that is older than a policy it enforces. - - *Relationship to security.txt.* An origin that already publishes a - "security.txt" file [RFC9116] SHOULD co-locate this manifest beside - it under "/.well-known/", so that an operator who maintains one - maintains the other and the two describe a consistent security - - - -Acosta Expires 8 February 2027 [Page 11] - -Internet-Draft Crypto-Agility Manifest August 2026 - - - posture. RFC 9116 does not define a field for pointing at an - arbitrary well-known resource, and its "Encryption" field is reserved - for a key or a certificate used to encrypt a report to the operator, - not for a posture document; a producer MUST NOT repurpose - "Encryption" to reference this manifest. Where an operator wishes to - make the link explicit, they MAY add a comment line to "security.txt" - naming the manifest's well-known location. A consumer that has - discovered a "security.txt" file SHOULD attempt to resolve this - manifest at its own well-known path independently, rather than - relying on any such reference. - -8. Security Considerations - - *A manifest is a claim, not a proof.* Nothing about serving a - manifest establishes that its contents are accurate. A consumer MUST - NOT treat a manifest as an authenticated statement of posture; a - consumer that requires assurance MUST obtain it from a verifiable - credential (see Section 7), not from the manifest alone. A hostile - or careless producer can overstate readiness, understate vulnerable - findings, or advertise deadlines it does not meet. A consumer that - gates a decision on an unverified manifest inherits that risk. - - *Self-declared conformance is not a pass.* The optional "conformance" - object (Section 5.10) lets a producer assert a "conformant" or - "conditional" status and enumerate accepted exceptions. This is a - producer's own assertion and carries no more weight than the - readiness score itself. A consumer MUST NOT treat a self-published - status as a pass, an approval, or evidence of accepted risk unless - the manifest also carries an "attestation" (Section 5.8) that covers - the asserted conformance and that the consumer has independently - verified. Absent a verified attestation, the "status" and - "exceptions" are informative context for a human reviewer, not an - authorization to proceed. - - *Do not leak internal detail.* The manifest is public. A producer - MUST NOT place secrets, key material, source snippets, file paths, - internal hostnames, or any non-public detail in it. The fields - defined here are deliberately coarse summaries for this reason. A - precise public inventory of which quantum-vulnerable algorithms a - system still uses, and where it is exposed to harvest-now-decrypt- - later, is also a roadmap for an attacker; a producer SHOULD weigh the - transparency benefit of a detailed posture against the reconnaissance - value it hands an adversary, and MAY publish only the coarse summary - this document defines. - - *Retrieve over TLS.* A manifest retrieved over the network SHOULD be - retrieved over HTTPS, so that a network attacker cannot substitute or - tamper with it in transit. A consumer SHOULD reject, or clearly mark - - - -Acosta Expires 8 February 2027 [Page 12] - -Internet-Draft Crypto-Agility Manifest August 2026 - - - as untrusted, a manifest retrieved over an unauthenticated channel. - As with "security.txt" [RFC9116], a cross-origin redirect encountered - while retrieving a manifest MUST NOT be treated as authoritative for - the original origin. - - *Freshness and denial of service.* A stale manifest can misrepresent - current posture; consumers SHOULD use "generatedAt" to bound - acceptable age. Because the manifest is fetched automatically and - potentially often, producers SHOULD serve it as a static resource, - and consumers SHOULD bound the size they will read and cache results, - so that manifest retrieval is not turned into a denial-of-service - vector against either party. - -9. IANA Considerations - - IANA is requested to register the following well-known URI in the - "Well-Known URIs" registry defined by [RFC8615]. - - URI suffix: crypto-agility.json - - Change controller: IETF - - Specification document(s): This document. - - Status: permanent - - Related information: The resource is a JSON [RFC8259] document with - media type "application/json". See Section 4. - - This document registers no new media type; the manifest uses the - existing "application/json" media type. - -10. Normative References - - [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate - Requirement Levels", BCP 14, RFC 2119, - DOI 10.17487/RFC2119, March 1997, - . - - [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC - 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, - May 2017, . - - [RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data - Interchange Format", STD 90, RFC 8259, - DOI 10.17487/RFC8259, December 2017, - . - - - - -Acosta Expires 8 February 2027 [Page 13] - -Internet-Draft Crypto-Agility Manifest August 2026 - - - [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: - Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002, - . - - [RFC8615] Nottingham, M., "Well-Known Uniform Resource Identifiers - (URIs)", RFC 8615, DOI 10.17487/RFC8615, May 2019, - . - - [RFC9110] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, - Ed., "HTTP Semantics", STD 97, RFC 9110, - DOI 10.17487/RFC9110, June 2022, - . - - [CycloneDX-CBOM] - OWASP CycloneDX / Ecma International TC54, "CycloneDX - Cryptography Bill of Materials (CBOM)", Cryptographic - assets in the CycloneDX Bill of Materials specification - (integrated in CycloneDX 1.6, extended in 1.7; published - as Ecma International standard ECMA-424), 2025, - . - -11. Informative References - - [RFC9116] Foudil, E. and Y. Shafranovich, "A File Format to Aid in - Security Vulnerability Disclosure", RFC 9116, - DOI 10.17487/RFC9116, April 2022, - . - - [RFC9958] Banerjee, A., Reddy.K, T., Schoinianakis, D., Hollebeek, - T., and M. Ounsworth, "Post-Quantum Cryptography for - Engineers", RFC 9958, DOI 10.17487/RFC9958, June 2026, - . - - [RFC7696] Housley, R., "Guidelines for Cryptographic Algorithm - Agility and Selecting Mandatory-to-Implement Algorithms", - BCP 201, RFC 7696, DOI 10.17487/RFC7696, November 2015, - . - - [NIST-CSWP-39] - National Institute of Standards and Technology, - "Considerations for Achieving Cryptographic Agility: - Strategies and Practices", NIST CSWP 39, - DOI 10.6028/NIST.CSWP.39, December 2025, - . - - - - - - - -Acosta Expires 8 February 2027 [Page 14] - -Internet-Draft Crypto-Agility Manifest August 2026 - - - [NIST-SP-800-56C] - Barker, E., Chen, L., and R. Davis, "Recommendation for - Key-Derivation Methods in Key-Establishment Schemes", NIST - SP 800-56C Rev. 2, DOI 10.6028/NIST.SP.800-56Cr2, August - 2020, . - - [NIST-IR-8547] - National Institute of Standards and Technology, - "Transition to Post-Quantum Cryptography Standards - (Initial Public Draft)", Initial Public Draft; deprecation - of quantum-vulnerable public-key algorithms after 2030 and - disallowance after 2035, NIST IR 8547 (ipd), November - 2024, . - - [NSM-10] The White House, "National Security Memorandum on - Promoting United States Leadership in Quantum Computing - While Mitigating Risks to Vulnerable Cryptographic Systems - (NSM-10)", National Security Memorandum 10; goal of - mitigating quantum risk to the maximum extent feasible by - 2035, May 2022, . - - [ISO-IEC-23837] - International Organization for Standardization / - International Electrotechnical Commission, "Information - security - Security requirements, test and evaluation - methods for quantum key distribution - Part 1: - Requirements; Part 2: Evaluation and testing methods", - ISO/IEC 23837-1:2023 and 23837-2:2023, 2023, - . - -Appendix A. Status of This Proposal - - This document is an individual Internet-Draft, the first step of the - IETF process, published to solicit review. It is not the product of - an IETF working group and does not represent IETF consensus, and it - is not yet a standard: it is a proposal put forward to be reviewed - and, if it has merit, advanced. The reference implementation, a - schema, and examples are maintained in the open at the repository - from which this draft originates. - -Author's Address - - - - - - -Acosta Expires 8 February 2027 [Page 15] - -Internet-Draft Crypto-Agility Manifest August 2026 - - - Leon Acosta - QuantaKrypto - Argentina - Email: leon@quantakrypto.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Acosta Expires 8 February 2027 [Page 16] diff --git a/draft/draft-acosta-crypto-agility-manifest-01.xml b/draft/draft-acosta-crypto-agility-manifest-01.xml deleted file mode 100644 index 6a27618..0000000 --- a/draft/draft-acosta-crypto-agility-manifest-01.xml +++ /dev/null @@ -1,843 +0,0 @@ - - - - - A Well-Known URI and JSON Format for Publishing Cryptographic Posture (the Crypto-Agility Manifest) - - - - QuantaKrypto -
- - Argentina - - leon@quantakrypto.com -
-
- - - - Security - crypto-agility - post-quantum - well-known URI - CBOM - cryptographic posture - - - - This document defines a discoverable, machine-readable JSON document, the - crypto-agility manifest, that a website or source repository publishes at - the well-known URI "/.well-known/crypto-agility.json" to declare its - cryptographic posture: a readiness summary, a compact Cryptography Bill of - Materials (CBOM) summary, an optional link to a posture attestation, the - migration policy it measures itself against, and an optional self-declared - conformance statement with justified exceptions. The manifest lets an automated - consumer, such as an AI coding agent, a continuous-integration bot, or an - auditor's tool, discover a project's crypto posture the way it already - discovers a security contact from "security.txt". The manifest is a public, - self-reported claim; it is not a proof. It is intended to complement, not - replace, a full CBOM inventory, serving as the CBOM's public-facing - discovery counterpart. - - - This document is a proposal. It is not an IETF product and is not a standard - of any kind. - - -
- - - -
- Introduction - - Crypto-agility, the capability to replace and adapt cryptographic algorithms - while preserving security and operations, is a mature and well-documented - objective. It is treated at the algorithm-agility level by - , and at the organizational and lifecycle level by - . Mechanism-level guidance for deriving keys when a - classical and a post-quantum exchange are combined appears in - , and security-evaluation criteria for quantum - key distribution in . The migration timelines that give crypto-agility - its present urgency are set out in and - , and general engineering guidance for the - post-quantum transition itself is collected in . The - inventory of what cryptography a system - actually uses now has a standard representation, the Cryptography Bill of - Materials (CBOM) defined in . - - - What is missing is not the concept and not the inventory format. It is a - conventional, discoverable place for a project to publish a short, public - summary of its posture that a machine can find and read without out-of-band - configuration. Automated agents increasingly make dependency and cryptography - choices, and continuous-integration systems increasingly gate on them, but - there is no standard location from which such a consumer can learn a project's - crypto posture, policy, and migration deadlines. Today every such integration - is bespoke. - - - This document fills exactly that gap and no more. It borrows its structural - model directly from "security.txt" : a small file at a - registered well-known location that any party can - fetch from an origin alone. Where "security.txt" publishes how to report a - vulnerability, the crypto-agility manifest publishes a project's cryptographic - posture. A full CBOM answers "what cryptography is in here"; the manifest is - its public-facing discovery counterpart, answering "where do I find this - project's posture, and what does it summarize to". - - - The manifest is deliberately a summary and not a replacement for a full CBOM - or a detailed readiness report. It is small enough to fetch and parse on every - CI run, and it answers three questions at a glance: how ready is this project, - what quantum-vulnerable cryptography is it still using, and by when has it - committed to migrate. A consumer that needs component-level detail follows the - CBOM pointer the manifest carries. - - -
- Scope and Non-Goals - - This document specifies the well-known URI, the media type, and the JSON - field definitions of the manifest. It does not define a new cryptographic - mechanism, a new attestation format, or a new CBOM format; it references the - CBOM format of for the summary it carries. - It does not specify how a producer computes a readiness score, and it does - not define the trust model of the credential an "attestation" link points - to. The manifest is a self-reported claim (see - ); establishing whether that claim is true is - out of scope and is the job of the linked attestation and of the consumer. - -
-
- -
- Terminology and Conventions - - The key words "MUST", "MUST NOT", - "REQUIRED", "SHALL", "SHALL NOT", - "SHOULD", "SHOULD NOT", - "RECOMMENDED", "NOT RECOMMENDED", - "MAY", and "OPTIONAL" in this document are to be - interpreted as described in BCP 14 - when, and only when, they appear in all capitals, as - shown here. - - - The following terms are used in this document: - -
-
manifest:
-
the JSON document defined by this specification.
-
producer:
-
the party or tool that generates and publishes a manifest.
-
consumer:
-
any party that retrieves and interprets a manifest, for example an agent, a - CI system, a scanner, or an auditor's tool.
-
origin:
-
as defined in , the scheme, host, and port from - which the manifest is retrieved.
-
CBOM:
-
a Cryptography Bill of Materials as defined in - .
-
classical public-key cryptography:
-
public-key algorithms, such as RSA and elliptic-curve Diffie-Hellman, whose - security is broken by a cryptographically relevant quantum computer.
-
- - Field names, JSON literals, and example values are shown in a monospaced font. - JSON types are as defined by the JSON grammar: object, array, string, number, - boolean, and null. In this document, "integer" denotes a JSON number with no - fractional part. - -
- -
- The Well-Known URI - - A producer publishes its manifest at the path "/.well-known/crypto-agility.json" - of an origin, that is: - - - - This location uses the well-known URI mechanism of - , the same mechanism that hosts "security.txt" - . Registration of the URI suffix "crypto-agility.json" - is requested in . A consumer that knows only an origin - MUST be able to locate the manifest by resolving this path - against that origin, with no out-of-band configuration. - - - A manifest published for a website or service SHOULD be served - from the origin's web root at the path above. A manifest that describes a - source repository MAY additionally be committed into the - repository at "/.well-known/crypto-agility.json" so that it travels with the - source and can be regenerated by continuous integration. - - - A manifest retrieved over HTTP or HTTPS SHOULD be retrieved over - HTTPS (see ). A consumer MUST NOT - follow a cross-origin redirect to locate a manifest without treating the result - as belonging to the redirect target rather than the original origin. - -
- -
- File Format - - The manifest is a single JSON object encoded in UTF-8. - When served over HTTP, it SHOULD be served with a "Content-Type" - header field of "application/json". A consumer - MUST reject a document that is not a single JSON object. - - - The top-level "version" field is the schema contract. A consumer - MUST branch on "version" and MUST reject a value - it does not understand. This document defines "version" 1. Additive, - backward-compatible fields MAY be introduced under "version" 1; - any breaking change bumps "version". A consumer MUST ignore - object members it does not recognize, so that additive fields do not break older - consumers. - - - The manifest is public by design. It MUST NOT contain secrets, - source snippets, file contents, or any data not intended for public disclosure - (see ). - -
- -
- Field Definitions - - The manifest object has the members defined in this section. Unless a member is - explicitly marked optional, it is REQUIRED and a conforming - producer MUST emit it. A machine-readable JSON Schema for - "version" 1 accompanies this specification. - - -
- version - - An integer. The manifest schema version. For this specification the value - MUST be 1. A consumer MUST reject a version it - does not understand. - -
- -
- manifestType - - A string. A discriminator that lets a consumer distinguish this document from - other JSON served at a well-known path. The value MUST be the - string "crypto-agility". A consumer MUST reject a document - whose "manifestType" is any other value. - -
- -
- generatedAt - - A string. The instant the manifest was generated, formatted as an - Internet timestamp per (a profile of ISO 8601). - A producer MUST set this from its runtime clock at generation - time. - -
- -
- generator - - An object describing what produced the manifest. It has two - REQUIRED string members: - -
-
name:
-
the name of the producing tool.
-
version:
-
the version of the producing tool.
-
-
- -
- subject - - An object identifying what the manifest describes. It has three - REQUIRED members: - -
-
root:
-
a string. The scanned root the manifest describes.
-
repository:
-
a string or null. The source repository the manifest describes. - MUST be null when unknown.
-
commit:
-
a string or null. The commit identifier the manifest was derived from. - MUST be null when unknown.
-
-
- -
- posture - - An object carrying the distilled cryptographic posture. It has four - REQUIRED members: - -
-
readinessScore:
-
a number from 0 to 100 inclusive. A producer-defined readiness score. A - value of 100 means no classical asymmetric cryptography was found. This - document does not define how the score is computed; a consumer - MUST NOT assume that scores from different producers are - directly comparable.
-
hybridKexInUse:
-
a boolean or null. Whether the subject negotiates hybrid post-quantum key - exchange. A producer that cannot determine this (for example a static - source scan, which cannot observe a negotiated key-exchange group) - MUST set null. A consumer MUST treat null as - "unknown", distinct from false.
-
quantumVulnerable:
-
an object with a REQUIRED integer member "total" (the count - of quantum-vulnerable findings) and a REQUIRED object member - "bySeverity". "bySeverity" MUST contain all five integer keys - "critical", "high", "medium", "low", and "info", each a count that - MUST NOT be negative.
-
hndlExposedCount:
-
an integer. The number of findings exposed to harvest-now-decrypt-later, - that is, data that can be recorded today and decrypted once a - cryptographically relevant quantum computer exists.
-
-
- -
- cbomSummary - - An object that summarizes, and points to, the full CBOM - from which it was derived. The full CBOM is - intentionally not inlined, so that the manifest stays small. It has three - REQUIRED members: - -
-
serialNumber:
-
a string. The CycloneDX "serialNumber" (a "urn:uuid:" value) of the full - CBOM this summary was derived from. This links the manifest to a specific - CBOM instance.
-
assetCount:
-
an integer. The number of distinct "cryptographic-asset" components in the - full CBOM.
-
algorithmFamilies:
-
an array of objects, ordered most-referenced first. Each element has a - REQUIRED string "family" (the algorithm family name, for - example "RSA" or "ECDH"), a REQUIRED integer "count" (the - number of references to that family), and a REQUIRED boolean - "quantumVulnerable".
-
-
- -
- attestation (optional) - - An OPTIONAL object. When present, it has one - REQUIRED string member: - -
-
url:
-
a URI locating a machine-verifiable posture credential. A producer - MUST record the URI verbatim and MUST NOT be - required to fetch it when generating the manifest. A producer - MUST omit the "attestation" member entirely when no credential - is declared, rather than emitting an empty or null value.
-
- - The presence of an "attestation" link does not by itself make the manifest - verifiable; it points a consumer to where verification can be performed. See - . - -
- -
- policy - - An object stating the migration policy the subject declares it is measured - against. It has five REQUIRED members: - -
-
source:
-
a string. A label for the policy source: a published standard's name, or - the literal "operator-declared" when the deadlines are set by the operator - rather than adopted from a standard.
-
deprecateClassicalAfter:
-
an integer year after which classical public-key cryptography is - considered deprecated under the declared policy.
-
disallowClassicalAfter:
-
an integer year after which classical public-key cryptography is - considered disallowed under the declared policy.
-
transitionDeadline:
-
a string or null. An operator-declared migration deadline (an - date or a year), or null when none is declared.
-
citation:
-
a string. A human-readable citation for the declared deadlines.
-
- - The values of "deprecateClassicalAfter" and "disallowClassicalAfter" are the - policy the subject holds itself to; they are a declaration, not a - verification. A producer that adopts a published timeline - SHOULD name it in "source" and "citation", for example the - 2030 and 2035 milestones of and - . National-security profiles such as the milestones of - are a valid "operator-declared" choice. - -
- -
- conformance (optional) - - An OPTIONAL object stating the subject's self-declared - conformance to the policy in , together with any - residual quantum-vulnerable findings it accepts. It lets a subject that is not - at a readiness score of 100 declare that its remaining classical cryptography - is known and accepted under the policy, not an oversight. When present, - it has two REQUIRED members and two OPTIONAL - members: - -
-
policy:
-
a string naming the policy the conformance is asserted against. It - SHOULD match the "source" member of the "policy" object.
-
status:
-
a string, one of "conformant", "conditional", or "nonconformant". - "conformant" means the subject meets the declared policy outright. - "conditional" means the subject does not meet the numeric bar, but every - residual quantum-vulnerable finding is enumerated in "exceptions" with a - justification. "nonconformant" means residual findings exist without accepted - exceptions.
-
statement:
-
an OPTIONAL string carrying a human-readable rationale for - the status.
-
exceptions:
-
an OPTIONAL array of the residual findings the subject - accepts. Each element is an object with a REQUIRED "family" - string (the algorithm family or finding the exception covers, for example - "RSA" or "X25519"), a REQUIRED "justification" string, and an - OPTIONAL "acceptedUntil" integer year (typically aligned with a - policy deadline) or null.
-
- - A self-published "conformance" status is a claim of the same kind as every other - value in this document, not a proof (). A consumer - MUST NOT treat a "conformant" or "conditional" status as assurance - unless the manifest also carries an "attestation" () that covers the asserted conformance and that the - consumer has verified. A producer that emits a "conformant" or "conditional" - status SHOULD also provide an "attestation". - -
-
- -
- Examples - - The following is a complete, conforming manifest for a repository that has - migrated most, but not all, of its key exchange to hybrid post-quantum key - exchange, still uses RSA and ECDH in two places, and adopts the - transition timeline. - - - - The "conformance" object above is optional. Because its "status" is - "conditional", the "attestation" link is what lets a consumer treat it as more - than a claim (). A producer that cannot observe - live key exchange (a static source scan) sets "hybridKexInUse" to null and omits - both "attestation" and "conformance" when neither is declared. - -
- -
- Operational Considerations - - The manifest is a self-reported claim. Every value in it is - asserted by the producer about itself. On its own the manifest establishes what - a project says about its posture, not what is true of it. This is the same - trust posture as "security.txt": publishing a file does not prove the - statements in it. - - - The "attestation" link is the bridge from claim to evidence. It points a - consumer to a machine-verifiable posture credential that a third party can - check independently of the manifest. A consumer that needs assurance rather - than a claim SHOULD follow "attestation.url" and verify the - credential there; the manifest itself SHOULD NOT be treated as - proof of the posture it reports. A manifest without an "attestation" member is - an unverified self-declaration and SHOULD be consumed as such. - - - The manifest and its CBOM summary SHOULD be derived from the same - underlying scan and CBOM, so that the summary can never contradict the full - CBOM referenced by "cbomSummary.serialNumber". A consumer that needs - component-level detail follows that pointer to the full CBOM rather than - relying on the summary. - - - Producers SHOULD regenerate the manifest as posture changes, for - example on each CI run, so that a stale manifest does not misrepresent a project - that has since migrated or regressed. "generatedAt" lets a consumer judge - freshness and reject a manifest that is older than a policy it enforces. - - - Relationship to security.txt. An origin that already publishes a - "security.txt" file SHOULD co-locate this - manifest beside it under "/.well-known/", so that an operator who maintains one - maintains the other and the two describe a consistent security posture. RFC 9116 - does not define a field for pointing at an arbitrary well-known resource, and its - "Encryption" field is reserved for a key or a certificate used to encrypt a - report to the operator, not for a posture document; a producer - MUST NOT repurpose "Encryption" to reference this manifest. Where an - operator wishes to make the link explicit, they MAY add a comment - line to "security.txt" naming the manifest's well-known location. A consumer that - has discovered a "security.txt" file SHOULD attempt to resolve this - manifest at its own well-known path independently, rather than relying on any such - reference. - -
- -
- Security Considerations - - A manifest is a claim, not a proof. Nothing about serving a - manifest establishes that its contents are accurate. A consumer - MUST NOT treat a manifest as an authenticated statement of - posture; a consumer that requires assurance MUST obtain it from a - verifiable credential (see ), not from the manifest - alone. A hostile or careless producer can overstate readiness, understate - vulnerable findings, or advertise deadlines it does not meet. A consumer that - gates a decision on an unverified manifest inherits that risk. - - - Self-declared conformance is not a pass. The optional - "conformance" object () lets a producer assert - a "conformant" or "conditional" status and enumerate accepted exceptions. This is - a producer's own assertion and carries no more weight than the readiness score - itself. A consumer MUST NOT treat a self-published status as a - pass, an approval, or evidence of accepted risk unless the manifest also carries - an "attestation" () that covers the asserted - conformance and that the consumer has independently verified. Absent a verified - attestation, the "status" and "exceptions" are informative context for a human - reviewer, not an authorization to proceed. - - - Do not leak internal detail. The manifest is public. A producer - MUST NOT place secrets, key material, source snippets, file - paths, internal hostnames, or any non-public detail in it. The fields defined - here are deliberately coarse summaries for this reason. A precise public - inventory of which quantum-vulnerable algorithms a system still uses, and where - it is exposed to harvest-now-decrypt-later, is also a roadmap for an attacker; - a producer SHOULD weigh the transparency benefit of a detailed - posture against the reconnaissance value it hands an adversary, and - MAY publish only the coarse summary this document defines. - - - Retrieve over TLS. A manifest retrieved over the network - SHOULD be retrieved over HTTPS, so that a network attacker cannot - substitute or tamper with it in transit. A consumer SHOULD - reject, or clearly mark as untrusted, a manifest retrieved over an unauthenticated - channel. As with "security.txt" , a cross-origin - redirect encountered while retrieving a manifest MUST NOT be - treated as authoritative for the original origin. - - - Freshness and denial of service. A stale manifest can - misrepresent current posture; consumers SHOULD use "generatedAt" - to bound acceptable age. Because the manifest is fetched automatically and - potentially often, producers SHOULD serve it as a static - resource, and consumers SHOULD bound the size they will read and - cache results, so that manifest retrieval is not turned into a denial-of-service - vector against either party. - -
- -
- IANA Considerations - - IANA is requested to register the following well-known URI in the "Well-Known - URIs" registry defined by . - -
-
URI suffix:
-
crypto-agility.json
-
Change controller:
-
IETF
-
Specification document(s):
-
This document.
-
Status:
-
permanent
-
Related information:
-
The resource is a JSON document with media type - "application/json". See .
-
- - This document registers no new media type; the manifest uses the existing - "application/json" media type. - -
- -
- - - - Normative References - - - - Key words for use in RFCs to Indicate Requirement Levels - - - - - - - - - - - Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words - - - - - - - - - - - The JavaScript Object Notation (JSON) Data Interchange Format - - - - - - - - - - - Date and Time on the Internet: Timestamps - - - - - - - - - - - Well-Known Uniform Resource Identifiers (URIs) - - - - - - - - - - HTTP Semantics - - - - - - - - - - - - - CycloneDX Cryptography Bill of Materials (CBOM) - - OWASP CycloneDX / Ecma International TC54 - - - - Cryptographic assets in the CycloneDX Bill of Materials - specification (integrated in CycloneDX 1.6, extended in 1.7; published as - Ecma International standard ECMA-424) - - - - - Informative References - - - - A File Format to Aid in Security Vulnerability Disclosure - - - - - - - - - - - Post-Quantum Cryptography for Engineers - - - - - - - - - - - - - - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms - - - - - - - - - - - Considerations for Achieving Cryptographic Agility: Strategies and Practices - - National Institute of Standards and Technology - - - - - - - - - - Recommendation for Key-Derivation Methods in Key-Establishment Schemes - - - - - - - - - - - - Transition to Post-Quantum Cryptography Standards (Initial Public Draft) - - National Institute of Standards and Technology - - - - - Initial Public Draft; deprecation of quantum-vulnerable public-key - algorithms after 2030 and disallowance after 2035 - - - - - National Security Memorandum on Promoting United States Leadership in Quantum Computing While Mitigating Risks to Vulnerable Cryptographic Systems (NSM-10) - - The White House - - - - National Security Memorandum 10; goal of mitigating quantum risk to - the maximum extent feasible by 2035 - - - - - Information security - Security requirements, test and evaluation methods for quantum key distribution - Part 1: Requirements; Part 2: Evaluation and testing methods - - International Organization for Standardization / International Electrotechnical Commission - - - - - - - -
- Status of This Proposal - - This document is an individual Internet-Draft, the first step of the IETF process, published to solicit review. It is not the product of an IETF working group and does not represent IETF consensus, and it is not yet a standard: it is a proposal put forward to be reviewed and, if it has merit, advanced. The reference implementation, a schema, and examples are - maintained in the open at the repository from which this draft originates. - -
-
- -
diff --git a/draft/draft-acosta-crypto-agility-manifest-02.md b/draft/draft-acosta-crypto-agility-manifest-02.md index 11adf8b..ae3b27d 100644 --- a/draft/draft-acosta-crypto-agility-manifest-02.md +++ b/draft/draft-acosta-crypto-agility-manifest-02.md @@ -1,6 +1,6 @@ # A Well-Known Crypto-Agility Manifest -`draft-acosta-crypto-agility-manifest-02` | individual Informational proposal | not submitted +`draft-acosta-crypto-agility-manifest-02` | individual Informational proposal | [publication status](https://datatracker.ietf.org/doc/draft-acosta-crypto-agility-manifest/) Author: Leon Acosta, QuantaKrypto, leon@quantakrypto.com @@ -24,7 +24,7 @@ A producer publishes the manifest at /.well-known/crypto-agility.json on an HTTP The relying party MUST configure the expected subject independently. For subject.kind origin, subject.id MUST equal the configured HTTPS origin serialized under [RFC6454]. Other kinds require an independently configured mapping from the discovery origin to the exact subject.id. Consumers MUST NOT infer authority over sibling hosts, repositories, fleets or deployment environments from a shared publisher. -Version 2 replaces, and is not an additive extension of, version 1. Both use the same discovery location. Producers serve one representation; consumers MUST reject unsupported versions rather than reinterpret their fields. A version 1 readiness score or hybridKex declaration MUST NOT be upgraded into a version 2 runtime observation. There is no downgrade or content-negotiation protocol in this document. +The contract identifier is the JSON integer version value 2. Producers serve one representation at the discovery location; consumers MUST reject unsupported versions rather than reinterpret their fields. Any adapter for a separately supported source format MUST retain its evidence provenance and MUST NOT promote a source declaration or score into a runtime observation. There is no downgrade or content-negotiation protocol in this document. Producers SHOULD supply ETag or Last-Modified validators and Cache-Control. Consumers SHOULD use conditional GET under [RFC9110] and [RFC9111], honor no-store and revalidation directives, and impose a local maximum cache age. A 304 response reuses bytes but MUST NOT extend manifest, evidence or observation validity. HTTP errors and unavailable manifests yield unknown posture, never unsupported cryptography. diff --git a/draft/draft-acosta-crypto-agility-manifest-02.txt b/draft/draft-acosta-crypto-agility-manifest-02.txt index d87c7d4..d82cfc9 100644 --- a/draft/draft-acosta-crypto-agility-manifest-02.txt +++ b/draft/draft-acosta-crypto-agility-manifest-02.txt @@ -4,8 +4,8 @@ Network Working Group L. Acosta Internet-Draft QuantaKrypto -Intended status: Informational 11 October 2026 -Expires: 14 April 2027 +Intended status: Informational 13 October 2026 +Expires: 16 April 2027 A Well-Known Crypto-Agility Manifest @@ -36,7 +36,7 @@ Status of This Memo time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." - This Internet-Draft will expire on 14 April 2027. + This Internet-Draft will expire on 16 April 2027. Copyright Notice @@ -53,7 +53,7 @@ Copyright Notice -Acosta Expires 14 April 2027 [Page 1] +Acosta Expires 16 April 2027 [Page 1] Internet-Draft Crypto-Agility Manifest October 2026 @@ -109,7 +109,7 @@ Table of Contents -Acosta Expires 14 April 2027 [Page 2] +Acosta Expires 16 April 2027 [Page 2] Internet-Draft Crypto-Agility Manifest October 2026 @@ -122,13 +122,13 @@ Internet-Draft Crypto-Agility Manifest October 2026 hosts, repositories, fleets or deployment environments from a shared publisher. - Version 2 replaces, and is not an additive extension of, version 1. - Both use the same discovery location. Producers serve one - representation; consumers MUST reject unsupported versions rather - than reinterpret their fields. A version 1 readiness score or - hybridKex declaration MUST NOT be upgraded into a version 2 runtime - observation. There is no downgrade or content-negotiation protocol - in this document. + The contract identifier is the JSON integer version value 2. + Producers serve one representation at the discovery location; + consumers MUST reject unsupported versions rather than reinterpret + their fields. Any adapter for a separately supported source format + MUST retain its evidence provenance and MUST NOT promote a source + declaration or score into a runtime observation. There is no + downgrade or content-negotiation protocol in this document. Producers SHOULD supply ETag or Last-Modified validators and Cache- Control. Consumers SHOULD use conditional GET under [RFC9110] and @@ -165,7 +165,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 3] +Acosta Expires 16 April 2027 [Page 3] Internet-Draft Crypto-Agility Manifest October 2026 @@ -221,7 +221,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 4] +Acosta Expires 16 April 2027 [Page 4] Internet-Draft Crypto-Agility Manifest October 2026 @@ -277,7 +277,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 5] +Acosta Expires 16 April 2027 [Page 5] Internet-Draft Crypto-Agility Manifest October 2026 @@ -333,7 +333,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 6] +Acosta Expires 16 April 2027 [Page 6] Internet-Draft Crypto-Agility Manifest October 2026 @@ -389,7 +389,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 7] +Acosta Expires 16 April 2027 [Page 7] Internet-Draft Crypto-Agility Manifest October 2026 @@ -445,7 +445,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 8] +Acosta Expires 16 April 2027 [Page 8] Internet-Draft Crypto-Agility Manifest October 2026 @@ -501,7 +501,7 @@ Appendix A. JSON Schema -Acosta Expires 14 April 2027 [Page 9] +Acosta Expires 16 April 2027 [Page 9] Internet-Draft Crypto-Agility Manifest October 2026 @@ -557,7 +557,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 10] +Acosta Expires 16 April 2027 [Page 10] Internet-Draft Crypto-Agility Manifest October 2026 @@ -613,7 +613,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 11] +Acosta Expires 16 April 2027 [Page 11] Internet-Draft Crypto-Agility Manifest October 2026 @@ -669,7 +669,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 12] +Acosta Expires 16 April 2027 [Page 12] Internet-Draft Crypto-Agility Manifest October 2026 @@ -725,7 +725,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 13] +Acosta Expires 16 April 2027 [Page 13] Internet-Draft Crypto-Agility Manifest October 2026 @@ -781,7 +781,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 14] +Acosta Expires 16 April 2027 [Page 14] Internet-Draft Crypto-Agility Manifest October 2026 @@ -837,7 +837,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 15] +Acosta Expires 16 April 2027 [Page 15] Internet-Draft Crypto-Agility Manifest October 2026 @@ -893,7 +893,7 @@ Internet-Draft Crypto-Agility Manifest October 2026 -Acosta Expires 14 April 2027 [Page 16] +Acosta Expires 16 April 2027 [Page 16] Internet-Draft Crypto-Agility Manifest October 2026 @@ -949,4 +949,4 @@ Author's Address -Acosta Expires 14 April 2027 [Page 17] +Acosta Expires 16 April 2027 [Page 17] diff --git a/draft/draft-acosta-crypto-agility-manifest-02.xml b/draft/draft-acosta-crypto-agility-manifest-02.xml index e03ffdc..d023d97 100644 --- a/draft/draft-acosta-crypto-agility-manifest-02.xml +++ b/draft/draft-acosta-crypto-agility-manifest-02.xml @@ -9,7 +9,7 @@ leon@quantakrypto.com - + This document defines a small JSON discovery manifest for cryptographic migration assessment. It distinguishes declarations, runtime observations and consumer verification, and separates key establishment from authentication. It covers heterogeneous endpoint profiles and evidence references. It does not define cryptographic negotiation, an attestation format, a scoring system, or authority to modify systems. @@ -26,7 +26,7 @@ A producer publishes the manifest at /.well-known/crypto-agility.json on an HTTPS origin, using application/json . An origin is a scheme, host and port tuple . This profile uses port 443 and DNS hostnames. Consumers MUST authenticate the HTTPS server and MUST NOT follow redirects automatically. Publication by an origin establishes only who served the declaration. The relying party MUST configure the expected subject independently. For subject.kind origin, subject.id MUST equal the configured HTTPS origin serialized under . Other kinds require an independently configured mapping from the discovery origin to the exact subject.id. Consumers MUST NOT infer authority over sibling hosts, repositories, fleets or deployment environments from a shared publisher. - Version 2 replaces, and is not an additive extension of, version 1. Both use the same discovery location. Producers serve one representation; consumers MUST reject unsupported versions rather than reinterpret their fields. A version 1 readiness score or hybridKex declaration MUST NOT be upgraded into a version 2 runtime observation. There is no downgrade or content-negotiation protocol in this document. + The contract identifier is the JSON integer version value 2. Producers serve one representation at the discovery location; consumers MUST reject unsupported versions rather than reinterpret their fields. Any adapter for a separately supported source format MUST retain its evidence provenance and MUST NOT promote a source declaration or score into a runtime observation. There is no downgrade or content-negotiation protocol in this document. Producers SHOULD supply ETag or Last-Modified validators and Cache-Control. Consumers SHOULD use conditional GET under and , honor no-store and revalidation directives, and impose a local maximum cache age. A 304 response reuses bytes but MUST NOT extend manifest, evidence or observation validity. HTTP errors and unavailable manifests yield unknown posture, never unsupported cryptography. Optional external change feeds MAY trigger a bounded refresh of the configured discovery origin. This document defines no feed URL, event schema, signing scheme or hash chain. Notifications MUST NOT replace snapshot validation, authorize execution, or redirect evidence fetching. Polling with backoff remains a recovery mechanism. diff --git a/tests/test_observatory_ingestion.py b/tests/test_observatory_ingestion.py index 7af0d98..c6c5bfd 100644 --- a/tests/test_observatory_ingestion.py +++ b/tests/test_observatory_ingestion.py @@ -25,7 +25,7 @@ def setUp(self): 'subjects': ['https://example.org'], 'audiences': ['monitor'], 'purposes': ['pqc-observation']}}} self.claims = {'iss': 'observatory', 'sub': 'https://example.org', 'aud': 'monitor', 'jti': 'measurement-1', 'purpose': 'pqc-observation', 'iat': int(NOW.timestamp()), 'exp': int(NOW.timestamp())+3600, - 'observation': {'profile': 'openssl-tls13-v1', 'observedAt': NOW.isoformat(timespec='seconds'), + 'observation': {'profile': 'openssl-tls13-v1', 'observedAt': NOW.isoformat(timespec='seconds').replace('+00:00', 'Z'), 'endpoint': {'host': 'example.org', 'port': 443, 'address': '192.0.2.1'}, 'probe': {'name': 'openssl', 'version': 'OpenSSL 3.5.0', 'groups': ['X25519MLKEM768', 'X25519'], 'certificateValidation': 'pkix-hostname', 'exitCode': 0}, @@ -59,6 +59,19 @@ def test_rejects_unbound_or_stale_measurements(self): claims['observation'][field] = value self.assertFalse(self.ingest(claims)['accepted']) + def test_signed_measurement_requires_whole_second_utc_profile(self): + malformed = ('2026-10-12T00:00:00+00:00', '2026-10-12T01:00:00+01:00', + '2026-10-12T00:00:00.000Z', '2026-10-12T00:00:00', + '2026-10-12 00:00:00Z', '2026-10-12T00:00:60Z', + '2026-02-30T00:00:00Z', 1791763200) + for i, value in enumerate(malformed): + with self.subTest(value=value): + claims = copy.deepcopy(self.claims) + claims['jti'] = 'timestamp-' + str(i) + claims['observation']['observedAt'] = value + self.assertEqual(self.ingest(claims), {'accepted': False, 'reason': 'invalid-measurement'}) + self.assertEqual(self.ingest(claims)['reason'], 'replay') + def test_rejects_nonvalidated_and_failed_probes(self): for i, (field, value) in enumerate([('exitCode', 1), ('exitCode', False), ('certificateValidation', 'disabled'), ('groups', ['X25519'])]): with self.subTest(field=field): diff --git a/tests/test_tls_live.py b/tests/test_tls_live.py index 8d22740..153e224 100644 --- a/tests/test_tls_live.py +++ b/tests/test_tls_live.py @@ -115,6 +115,7 @@ def test_real_measurement_immediately_signed_and_ingested(self): from tools.observatory_ingestion import ingest_measurement result = self.run_connection('X25519', ['X25519']) self.assertEqual(result['status'], 'verified-connection') + self.assertRegex(result['evidence']['observedAt'], r'^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-5][0-9]Z$') subject = 'https://localhost:' + str(result['port']) key = Ed25519PrivateKey.generate() public = key.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw) diff --git a/tools/observatory_ingestion.py b/tools/observatory_ingestion.py index 607703d..def4391 100644 --- a/tools/observatory_ingestion.py +++ b/tools/observatory_ingestion.py @@ -6,6 +6,7 @@ import datetime as dt import hashlib import ipaddress +import re from urllib.parse import urlsplit from tools.attestation import verify_envelope from tools.openssl_observation import parse_openssl_observation, MAX_TRANSCRIPT_BYTES, HYBRID_GROUPS, CLASSICAL_GROUPS @@ -41,7 +42,10 @@ def ingest_measurement(token, transcript, trust_config, expected_subject, *, exp if (subject.scheme != 'https' or subject.username is not None or subject.password is not None or subject.query or subject.fragment or subject.path not in ('', '/') or subject.hostname != endpoint['host'].lower() or (subject.port or 443) != endpoint['port']): raise ValueError('endpoint-subject-mismatch') - observed = dt.datetime.fromisoformat(observation['observedAt'].replace('Z', '+00:00')) + timestamp = observation['observedAt'] + if not isinstance(timestamp, str) or not re.fullmatch(r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-5][0-9]Z', timestamp): + raise ValueError('invalid-measurement-time') + observed = dt.datetime.fromisoformat(timestamp.replace('Z', '+00:00')) if observed.tzinfo is None or not dt.timedelta(0) <= now-observed <= dt.timedelta(seconds=max_age_seconds): raise ValueError('stale-or-future-measurement') if observed.timestamp() > claims['iat']: diff --git a/tools/render_draft.py b/tools/render_draft.py index 4a829e8..c90fc32 100644 --- a/tools/render_draft.py +++ b/tools/render_draft.py @@ -19,7 +19,7 @@ def inline(element): def render(path=XML): root = ET.parse(path).getroot() lines = ['# ' + root.findtext('front/title'), '', - '`' + root.attrib['docName'] + '` | individual Informational proposal | not submitted', '', + '`' + root.attrib['docName'] + '` | individual Informational proposal | [publication status](https://datatracker.ietf.org/doc/draft-acosta-crypto-agility-manifest/)', '', 'Author: Leon Acosta, QuantaKrypto, leon@quantakrypto.com', '', '', '', '## Abstract', '', root.findtext('front/abstract/t'), ''] diff --git a/tools/tls_probe.py b/tools/tls_probe.py index 7fb138d..b8eb4f3 100644 --- a/tools/tls_probe.py +++ b/tools/tls_probe.py @@ -103,7 +103,7 @@ def probe_openssl(host, port=443, timeout=5, *, groups=DEFAULT_GROUPS, parsed['negotiatedGroup'] in groups and parsed['peerSignatureType'] is not None) hybrid = verified and parsed['hybridKexObserved'] classical = verified and parsed['negotiatedGroup'] in CLASSICAL_GROUPS - observed_at = dt.datetime.now(dt.timezone.utc).isoformat(timespec='seconds') + observed_at = dt.datetime.now(dt.timezone.utc).isoformat(timespec='seconds').replace('+00:00', 'Z') return {**parsed, 'host': host, 'port': port, 'address': str(ip), 'observedAt': observed_at, 'status': 'verified-connection' if verified else 'failed', 'certificateValidated': verified, 'hybridKexVerified': hybrid,