# Standards

> **A tooled self-assessment, not a certification.** Every ISO/IEC, ITU-T and IETF standard pkinative touches, the evidence that holds each — a test, a rule or a conformance level — and the gaps it leaves.

No body has audited or certified pkinative against any standard on this page. What each row records is the evidence the repository itself runs — a test file, a `verify:docs` rule or a level of the conformance gate — and the gaps that evidence leaves. Every path and rule named here is checked to exist by the `standards-evidence` rule, so the page cannot cite evidence that has gone away; whether the evidence is *sufficient* is your judgement.

The claimed profile is the IETF one: RFC 5280 for certificates, and the RFCs below for everything built on them. The ISO/IEC and ITU-T standards appear because RFC 5280 is written on top of them. Most ISO/IEC standards on this page are published as common text with an ITU-T Recommendation; both numbers are given, and the editions are the ones in force when this page was written (X.680 and X.690: 02/2021; X.509 and X.520: 10/2019).

How to read the status column: **applies** — pkinative implements the standard for what it claims and holds itself to it; **partial** — it implements part, and the gap column says which; **not claimed** — the standard concerns an organisation or an artefact pkinative does not produce, and nothing in the repository claims it.

## ASN.1 and the directory

| Standard | Scope for pkinative | Status | Evidence | Gaps |
|---|---|---|---|---|
| ITU-T X.690 \| ISO/IEC 8825-1 — BER, CER and DER | Decoding DER (strict, the default) and BER (on request); encoding DER | applies | `src/asn1/`; `tests/asn1/asn1-decode.test.ts`, `tests/asn1/asn1-encode.test.ts`, `tests/asn1/asn1-read.test.ts`; `tests/fuzzing/asn1-lengths-tags-nesting.test.ts`, `tests/fuzzing/asn1-values.test.ts`; `tests/property/asn1-roundtrip.test.ts`; byte identity at `L2` against `scripts/lib/raw-der.ts`, an independent walker; the §11.6 and §11.2.2 clauses `x690-11.6-rdn-set-sorted` and `x690-11.2.2-named-bits-trimmed` at `L5` | §11.2.2, §11.5 and §11.6 departures are diagnostics, refused only under `strict: true` (decided: real issuers commit them, and they read the same in every parser). CER is neither implemented nor claimed. The X.690 quotes in `scripts/lib/clauses.ts` are not machine-checked against a pinned text, as the RFC 5280 ones are. |
| ITU-T X.680 \| ISO/IEC 8824-1 — ASN.1 notation | The universal tags and the value sets the readers accept | applies | `src/asn1/asn1-tags.ts`, `src/core/text.ts` (the PrintableString set, diagnostic `PKI_DIAG_PRINTABLE_STRING_CHARSET`); a typed reader for every universal type a PKI structure carries, ENUMERATED (`readEnumerated`, X.690 §8.4) and RELATIVE-OID (`readRelativeOid`, `encodeRelativeOid`, X.690 §8.20) included; `tests/asn1/asn1-tags.test.ts`, `tests/asn1/asn1-read.test.ts`, `tests/asn1/asn1-oid.test.ts`, `tests/asn1/asn1-time.test.ts`, `tests/property/asn1-roundtrip.test.ts` | Not read, because no PKI structure pkinative reads carries them: REAL has no reader, and under BER the GeneralizedTime forms X.680 allows beyond RFC 5280 §4.1.2.5.2 — hour-only times, fractions of a minute or an hour, numeric offsets — are refused. |
| ITU-T X.509 \| ISO/IEC 9594-8 — certificate framework | Read through the RFC 5280 profile, never beyond it | partial | Everything under RFC 5280 below | Extensions X.509 defines and RFC 5280 does not are kept undecoded (`kind: 'unknown'`). Attribute certificates are not read. |
| ITU-T X.520 \| ISO/IEC 9594-6 — attribute types | Names: every DirectoryString choice is read, each attribute held to its syntax and its upper bound | applies | `src/x509/x509-name.ts`, with the RFC 5280 Appendix A.1 syntax and `ub-*` table in `src/core/name-oids.ts` and the ISO 3166-1 codes in `src/x509/iso3166.ts`; `tests/x509/x509-name.test.ts` (`PKI_DIAG_NAME_ATTRIBUTE_STRING_TYPE`, `PKI_DIAG_COUNTRY_NAME_SIZE`, `PKI_DIAG_NAME_ATTRIBUTE_TOO_LONG`, `PKI_DIAG_NAME_COUNTRY_UNKNOWN`), `tests/x509/iso3166.test.ts`, `tests/build/build-structures.test.ts`; the attribute names in `src/oid/` | Diagnosed, never enforced when reading: a value outside its Appendix A.1 string type raises `PKI_DIAG_NAME_ATTRIBUTE_STRING_TYPE` and a `countryName` that is not two characters `PKI_DIAG_COUNTRY_NAME_SIZE`, each refused only under `strict: true`; a value past its `ub-*` upper bound, counted in characters, raises `PKI_DIAG_NAME_ATTRIBUTE_TOO_LONG` and is read as it is, information even under `strict`. The bounds are RFC 5280 Appendix A.1's (the five X520name attributes at ub-name, 32768), not the X.400 personal-name lengths. `encodeNameAttribute` refuses a wrong type or size when writing. |
| ITU-T X.660 \| ISO/IEC 9834-1 — object identifier arcs | Encoding, decoding and validating OIDs | applies | `src/asn1/asn1-oid.ts`; `tests/asn1/asn1-oid.test.ts`, `tests/oid/oid-registry.test.ts`; `tests/fuzzing/asn1-values.test.ts` | — |

## Character sets and data formats

| Standard | Scope for pkinative | Status | Evidence | Gaps |
|---|---|---|---|---|
| ISO/IEC 10646 (the Unicode repertoire) | UTF8String, BMPString and UniversalString decoding | applies | `src/core/text.ts`; `tests/core/text.test.ts`: overlong UTF-8, surrogates and values past U+10FFFF are refused; `tests/asn1/asn1-read.test.ts`: odd lengths are refused; `tests/asn1/asn1-read-iso10646.test.ts`: a byte-order signature and an ISO/IEC 2022 escape control are diagnosed (`PKI_DIAG_STRING_SIGNATURE`, `PKI_DIAG_STRING_ESCAPE_SEQUENCE`) | A byte-order signature at the start of a BMPString or UniversalString, or an escape control inside any of the three types, is diagnosed and decoded as it is; it is refused only under `strict: true`. |
| ITU-T T.61 (TeletexString) | Read for interoperability only | partial, by design | `PKI_DIAG_TELETEX_AS_LATIN1`, in `docs/data/diagnostics.json` | T.61 is not implemented: a TeletexString is read as Latin-1 and the diagnostic says so. |
| ISO 3166-1 alpha-2 (country codes) | `countryName` values | applies | `src/x509/iso3166.ts` (the 249 officially assigned codes of ISO 3166-1:2020; the user-assigned range AA, QM–QZ, XA–XZ, ZZ of §8.1.3 admitted), `src/x509/x509-name.ts`; `tests/x509/iso3166.test.ts` (the count, the range, the reserved codes UK, EU, AN refused), `tests/x509/x509-name.test.ts` (`PKI_DIAG_NAME_COUNTRY_UNKNOWN`), `tests/build/build-structures.test.ts` | Information, not refusal: a two-letter code the list does not hold is reported (`PKI_DIAG_NAME_COUNTRY_UNKNOWN`) and read as it is, even under `strict: true`; a wrong size is `PKI_DIAG_COUNTRY_NAME_SIZE`. The table follows the ISO 3166 Maintenance Agency by hand, so a code assigned after it was written is reported as unknown until the table does. `encodeNameAttribute` checks the size, not the list. |
| RFC 5280 §4.1.2.5 time profile (and ISO 8601 in spirit) | UTCTime and GeneralizedTime in certificates, lists and messages | applies | `src/asn1/asn1-time.ts`; `tests/asn1/asn1-time.test.ts`; the clauses `4.1.2.5-generalized-time-only-from-2050` and `4.1.2.5.2-generalized-time-no-fraction` at `L5` | — |

## The RFCs pkinative claims

| Standard | Scope for pkinative | Status | Evidence | Gaps |
|---|---|---|---|---|
| RFC 5280 — certificates and CRLs | Parsing, §6 path validation, §5 revocation | applies | `L0` to `L8` in the [conformance guide](conformance.md): x509-limbo, NIST PKITS and its S/MIME messages; the pinned text of §4.1–4.2 held clause by clause (`scripts/lib/clauses.ts`, `scripts/data/rfc5280-requirements.json`, `tests/conformance/clauses.test.ts`, `tests/conformance/rfc-requirements.test.ts`); `tests/x509/x509-certificate.test.ts`, `tests/path/path-validate.test.ts`, `tests/revocation/crl-check.test.ts`; the rules `clause-table-complete` and `refusal-baseline-frozen` | The reviewed deviations of `scripts/data/pkits-score.json` and `scripts/data/limbo-score.json`; distinguished names compared by encoded bytes rather than by §7.1 preparation ([ADR 0005](../adr/0005-names-compared-by-encoded-bytes.md)). |
| RFC 2986 — PKCS#10 certification requests (with RFC 2985 §5.4 for `extensionRequest` and `challengePassword`) | Reading, verifying the proof of possession, and building | applies | `src/x509/x509-csr.ts`, `src/verify/verify-csr.ts`; `tests/x509/x509-csr.test.ts`, `tests/verify/verify-csr.test.ts`, `tests/fuzzing/csr.test.ts`, `tests/build/build-certificate.test.ts`; `recipes/csr.ts`; the `openssl:csr` case of the [interoperability matrix](conformance.md#the-write-direction) reads back and verifies every request `openssl req -new` writes, and OpenSSL, .NET, GnuTLS, Go, Python cryptography and Java read the ones pkinative writes | Attributes other than `extensionRequest` and `challengePassword` are kept as DER (`CsrAttribute`); a request is held to the certificate's codes and bounds, so a `version` other than 0 is `PKI_X509_VERSION_INVALID` and the attributes count against `maxExtensions`. A valid request proves possession of a key and nothing about who sent it. |
| RFC 5652 — CMS SignedData | Reading, building and verifying | applies | `tests/cms/cms-signed-data.test.ts`, `tests/cms/cms-check.test.ts`, `tests/verify/verify-signed-data.test.ts`, `tests/fuzzing/cms.test.ts`; `L8`, 224 PKITS S/MIME messages verified whole; the pinned text of §5 and §11.1–11.4 held sentence by sentence to a named test (`scripts/data/rfc5652-requirements.json`) | DSA signers are not verified ([ADR 0004](../adr/0004-dsa-and-ed448-cms-signers-not-verified.md)); an Ed448 signer is verified, and written by `createSignedData`, where the host has Ed448 — Node.js — and reported unchecked, or refused with `PKI_CRYPTO_KEY_UNSUPPORTED`, where it does not — Bun, Chromium ([ADR 0022](../adr/0022-ed448-cms-signers-host-dependent.md)); countersignatures are carried, not verified — their signed attributes are looked into only as far as the §11.4 diagnostics need. |
| RFC 6211 — CMS algorithm protection; RFC 5035 — signing-certificate-v2 | Checked on verification | applies | `tests/cms/cms-check.test.ts`, `tests/cms/cms-attributes.test.ts` | — |
| RFC 3161 and RFC 5816 — timestamps | Requests, responses, tokens and their verification | applies | `tests/cms/tsp-request.test.ts`, `tests/cms/tsp-response.test.ts`, `tests/cms/tsp-tst-info.test.ts`, `tests/verify/verify-timestamp.test.ts`; `recipes/timestamp.ts`; the pinned text of RFC 3161 §2.3–2.4 held sentence by sentence (`scripts/data/rfc3161-requirements.json`) | One level of evidence: no ETSI long-term formats ([ADR 0009](../adr/0009-no-etsi-long-term-signature-formats.md)); the certReq contract between request and token is a diagnostic of `verifyTimeStampToken`, never a reason. |
| RFC 6960 — OCSP | Requests, responses and the client's §3.2 checks | applies | `tests/revocation/ocsp.test.ts`, `tests/revocation/ocsp-check.test.ts`, `tests/fuzzing/reports.test.ts`; the pinned text of §4.1–4.2 held sentence by sentence (`scripts/data/rfc6960-requirements.json`) | No network I/O: the caller fetches ([ADR 0006](../adr/0006-no-network-io-in-the-engine.md)); the responderID is compared with the signer only when `checkOcspStatus` is given it, as `verifyCertificateChain` does, and the comparison is a diagnostic, never a reason. |
| RFC 7468 — PEM | Strict and lax decoding, encoding | applies | `tests/pem/pem.test.ts`, `tests/fuzzing/pem-malformed.test.ts`; the pinned text of §2–5 held sentence by sentence (`scripts/data/rfc7468-requirements.json`) | Strict mode refuses the whitespace §2 asks parsers to ignore; lax mode accepts it with a diagnostic. |
| RFC 5958 — PKCS#8; RFC 8018 — PBES2 and PBKDF2 | Reading and opening keys | applies, read only | `tests/keys/key-pkcs8.test.ts`, `tests/keys/key-pbes2.test.ts`, `tests/keys/key-import.test.ts`; the rule `key-operation-parity` | No writer ([ADR 0003](../adr/0003-no-pkcs8-or-pkcs12-writer.md)); PBES2 with AES-CBC only. |
| RFC 7292 — PKCS#12; RFC 9579 — PBMAC1 | Opening `.p12` files | partial, by design | `tests/keys/key-pkcs12.test.ts`, `tests/verify/verify-pkcs12.test.ts`, `tests/crypto/webcrypto-password.test.ts`, `tests/fuzzing/pkcs12.test.ts`; the rule `pkcs12-policy-parity`; the pinned text of §4–5 held sentence by sentence (`scripts/data/rfc7292-requirements.json`) | PBES2 only: the Appendix B KDF and the Appendix C ciphers are refused ([ADR 0002](../adr/0002-pkcs12-pbes2-only.md)), so an Appendix B MAC is reported unverified. |
| RFC 3279, RFC 4055, RFC 5480, RFC 8410, RFC 8017 — algorithms and keys | Reading key and algorithm identifiers; signatures through Web Crypto | applies | `tests/x509/x509-spki.test.ts`, `tests/crypto/crypto-algorithms.test.ts`, `tests/crypto/x509-verify.test.ts`, `tests/build/build-structures.test.ts` | An `id-RSASSA-PSS` public key, which no conforming Web Crypto imports under that identifier, is handed to the host under `rsaEncryption` once the signature has been held to the key's parameters (RFC 4055 §1.2, §3.3), so what it signed verifies everywhere; an `id-RSASSA-PSS` private key under PBES2 still depends on the host, since a shrouded key is never re-wrapped. No default RSA scheme ([ADR 0015](../adr/0015-no-default-rsa-scheme.md)). |
| FIPS 204 — ML-DSA keys | Reading the SubjectPublicKeyInfo | applies, read only | `tests/x509/x509-spki.test.ts` | ML-DSA signatures are not verified. |
| RFC 6125 — host names | Matching a certificate to a host | applies | `tests/path/path-server-name.test.ts` | No public suffix list, by design. |

## Process and supply chain

| Standard | Scope for pkinative | Status | Evidence | Gaps |
|---|---|---|---|---|
| ECMA-424 (CycloneDX) | The SBOM attached to each GitHub release | partial | `.github/workflows/publish.yml` writes it with `npm sbom --sbom-format cyclonedx` | `npm sbom` writes CycloneDX 1.5 (measured with npm 10.9.2); the current edition of ECMA-424 (December 2025) specifies CycloneDX 1.7. The SBOM is CycloneDX, not claimed as ECMA-424. |
| ISO/IEC 5962 (SPDX) | Licence identification of every file, and an SPDX SBOM of each release | applies | `REUSE.toml` gives every file an SPDX licence identifier and copyright (REUSE 3.3; `reuse lint` reports the project compliant), held by the rule `reuse-shape`; `.github/workflows/publish.yml` writes an SPDX SBOM (pkinative-X.Y.Z.spdx.json) beside the CycloneDX one, attested and attached to the release (`tests/tools/workflows.test.ts`) | The SBOM of the runtime is empty by design (zero dependencies); the build toolchain has its own CycloneDX SBOM, not an SPDX one. |
| ISO/IEC 5230 and ISO/IEC 18974 (OpenChain) | Open source licence compliance and security assurance programmes | not claimed | `LICENSE`, `REUSE.toml` (every file's licence, the foreign fixtures under their own terms, rule `reuse-shape`), `THIRD-PARTY-NOTICES.md` (the corpora, their licences and pins, held by the rule `corpus-pin-parity`); zero runtime dependencies | These certify an organisation's programme; a single-maintainer library has no such programme to certify. |
| ISO/IEC 29147 (vulnerability disclosure) and ISO/IEC 30111 (vulnerability handling) | How a report reaches the maintainer and is handled | applies (self-assessed) | `SECURITY.md` §ISO/IEC 29147 and 30111 self-assessment: each clause of 29147 §5–§7 and 30111 §7.1–§7.5 mapped to where the repository meets it — the policy and `docs/.well-known/security.txt`, the 48-hour receipt, the verification and remediation steps, the advisory, the CVE-class corpus `docs/data/cve-classes.json` | A self-assessment by one maintainer against the clauses, with no certification and no second party: both standards describe an organisation's processes, and the evidence is the repository's own record, not an audited one. |
| ISO/IEC 40500 (WCAG) | The documentation site | applies (WCAG 2.2 A and AA, automated rules) | The rules `contrast` (4.5:1 text contrast in both palettes) and `a11y-structure` (skip link, one `<main>` and `<h1>`, heading levels, `alt`, button names); `lang="en"` on every page; the `a11y` job of the Docs workflow runs axe-core 4.13.0 (WCAG 2.0/2.1/2.2 A and AA plus best practices) over every page in the light and the dark palette, zero violations blocking (`scripts/a11y-check.ts`) | Automated rules decide about half of WCAG; the manual criteria (meaningful sequence, reading level, consistent navigation) are reviewed, not measured. |
| ISO/IEC 27001 | Information security management | not claimed | — | It certifies an organisation's management system; a library cannot conform to it. |

## What would change a row

A row moves when its evidence moves: a new test or rule is added to the evidence column in the same commit, and a gap closed by a change is removed in that change. The page claims nothing that the repository does not run; a certification, if one is ever obtained, will be named here with its certificate number and scope.
