Skip to content

feat: add CRL support in generation and validation - #987

Open
Manuthor wants to merge 4 commits into
developfrom
fix/put_kms_public_url_in_allowed_cors_by_default
Open

feat: add CRL support in generation and validation#987
Manuthor wants to merge 4 commits into
developfrom
fix/put_kms_public_url_in_allowed_cors_by_default

Conversation

@Manuthor

@Manuthor Manuthor commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

1 — X.509 CRL generation

A new GenerateCrl KMIP-like operation builds an X.509 v2 CRL for a given CA
certificate. It queries all revoked certificates (state Deactivated or
Compromised) whose CertificateLink points to the issuer, signs the CRL
with the issuer's private key, and returns the DER bytes.

The CRL is also written to an in-memory per-issuer cache so the
unauthenticated GET /public/certificates/{id}/crl endpoint can serve it
without access to key material. The cache is refreshed on every authenticated
GenerateCrl call and cleared on server restart.

CLI: ckms certificates generate-crl --issuer-certificate-id <ID>.


2 — PQC key format conversions (crate/crypto/src/crypto/pqc/mod.rs)

Adds two public converters for all PQC algorithm families
(ML-DSA-44/65/87, ML-KEM-512/768/1024, SLH-DSA all 12 variants,
hybrid KEMs X25519MLKEM768 / X448MLKEM1024):

Function What it does
pqc_private_key_pkcs8_to_raw(pkcs8_der) Parses a PKCS#8 DER private key and extracts the raw seed bytes via EVP_PKEY_get_raw_private_key
pqc_public_key_spki_to_raw(spki_der) Parses a SubjectPublicKeyInfo DER public key and extracts raw bytes via EVP_PKEY_get_raw_public_key

These converters allow any KMIP client that requests a PQC key in
KeyFormatType::Raw (required for ML-KEM encapsulation in hybrid TLS, or for
interop with libraries that do not parse DER) to get raw seed bytes from a key
that was generated or imported in PKCS#8 / SPKI form. RAII guards (PKeyGuard,
BioGuard) ensure no key material leaks on error paths.


3 — Security advisories

Advisory Severity Fix
GHSA-rwc8-xwm6-52xc Applied patch

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands PKI support across server, CLI, Web UI, and tests by adding CRL generation + public CRL distribution, strengthening CRL-based validation behavior, and updating related KMIP/TTLV and export paths (including PQC Raw export support).

Changes:

  • Add CRL generation (authenticated) and public CRL distribution point endpoint (unauthenticated), plus auto-injection of CDP into issued certificates when kms_public_url is set.
  • Strengthen certificate validation around CRLs (freshness checks, signature handling) and persist revocation reasons needed for CRL entries; fix TTLV normalization for structured RevocationReason.
  • Add PQC key export as KeyFormatType::Raw (non-FIPS) plus new vectors/tests and related documentation/CORS behavior updates.

Reviewed changes

Copilot reviewed 37 out of 38 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
ui/src/menuItems.tsx Adds a new “Generate CRL” menu entry under Certificates.
ui/src/App.tsx Registers a new React route for the Generate CRL form.
ui/src/actions/Certificates/CertificateGenerateCrl.tsx New UI form that calls the CRL REST endpoint and downloads DER/PEM.
documentation/docs/use_cases/pki.md Documents CRL generation and clarifies unsupported Delta CRLs.
documentation/docs/configuration/tls.md Adds ANSSI TLS recommendations note.
documentation/docs/configuration/server_configuration_file.md Clarifies CORS defaults and interaction with kms_public_url.
crate/test_kms_server/src/vector_runner.rs Extends vector runner with {{hex:...}}, AllocTempFile, and GenerateCrl steps; adds new vector tests.
crate/test_kms_server/src/pqc_export_tests.rs New non-FIPS PQC export-as-Raw roundtrip tests.
crate/test_kms_server/src/lib.rs Wires in new CRL and PQC export test modules.
crate/test_kms_server/src/crl_tests.rs New integration tests covering CRL generation and CRL-based validation lifecycle.
crate/test_kms_server/README.md Updates vector counts and lists new vectors.
crate/test_kms_server/Cargo.toml Adds deps needed by new tests/vector-runner capabilities.
crate/server/src/start_kms_server.rs Registers CRL routes (authenticated + public) in Actix app/scope.
crate/server/src/routes/mod.rs Adds the new crl routes module.
crate/server/src/routes/crl.rs Implements authenticated CRL generation and public cached CRL serving.
crate/server/src/core/operations/validate.rs Tightens CRL validation handling (freshness checks, signature rules, file:// handling, soft-fail for unreachable CRLs).
crate/server/src/core/operations/revoke.rs Persists revocation reasons/metadata needed for CRL generation.
crate/server/src/core/operations/mod.rs Exposes the new generate_crl operation module.
crate/server/src/core/operations/generate_crl.rs Implements CRL generation, selection of revoked certs, and in-memory caching for public serving.
crate/server/src/core/operations/export_get.rs Adds non-FIPS PQC PKCS#8/SPKI → Raw conversion during export.
crate/server/src/core/operations/certify/subject.rs Adds detection of existing CDP extension to avoid injecting duplicates.
crate/server/src/core/operations/certify/certify_op.rs Passes kms_public_url into certificate building for CDP injection.
crate/server/src/core/operations/certify/build_certificate.rs Injects server CDP extension when appropriate; adds DER length helper.
crate/server/src/config/params/server_params.rs Automatically includes kms_public_url in default CORS allow-list when cors_allowed_origins is unset + adds unit tests.
crate/server/documentation/openapi.yaml Documents new CRL endpoints in OpenAPI.
crate/kmip/src/ttlv/normalize.rs Fixes AttributeValue normalization to preserve structured types; adds regression tests.
crate/kmip/src/kmip_0/kmip_types.rs Adds vendor-extension revocation reason codes required for CRL completeness.
crate/crypto/src/openssl/mod.rs Exposes new OpenSSL CRL module.
crate/crypto/src/openssl/crl.rs New OpenSSL-sys based CRL builder implementation + unit tests.
crate/crypto/src/crypto/pqc/mod.rs Adds PKCS#8/SPKI → Raw extraction helpers + tests for conversion roundtrips.
crate/crypto/Cargo.toml Adds deps needed by new OpenSSL CRL builder module.
crate/clients/client/src/kms_rest_client.rs Adds get_bytes() helper for non-JSON REST responses (DER/PEM).
crate/clients/client/src/http_client/login.rs Fixes OAuth callback server startup race by pre-binding the callback port.
crate/clients/clap/src/actions/certificates/mod.rs Registers new ckms certificates generate-crl command.
crate/clients/clap/src/actions/certificates/generate_crl.rs Implements CLI action calling REST CRL endpoint and writing output file.
crate/clients/ckms/src/tests/certificates/certify.rs Updates test expectations for CDP URL fixture.
CHANGELOG/fix_put_kms_public_url_in_allowed_cors_by_default.md Adds branch-specific changelog entries covering CRL, PQC export, CORS, and related changes.
Cargo.lock Updates lockfile for newly added dependencies.

Comment thread crate/server/src/routes/crl.rs
Comment thread crate/server/src/routes/crl.rs Outdated
Comment thread crate/server/src/core/operations/validate.rs Outdated
Comment thread crate/server/src/core/operations/certify/build_certificate.rs Outdated
Comment thread crate/server/src/core/operations/generate_crl.rs Outdated
Manuthor added a commit that referenced this pull request Jun 7, 2026
Manuthor added a commit that referenced this pull request Jun 8, 2026
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch from acfb590 to 8f2ebff Compare June 8, 2026 07:01
Manuthor added a commit that referenced this pull request Jun 8, 2026
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch from 629b34d to 1c7fd8d Compare June 8, 2026 12:55
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch from 05a4a4f to a7e4fe5 Compare August 12, 2026 19:52
@Manuthor
Manuthor changed the base branch from develop to feat/split_key August 12, 2026 19:52
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch from d40a530 to 089f3a2 Compare August 13, 2026 05:58
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch from 089f3a2 to 4714b9b Compare August 13, 2026 13:04
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch 3 times, most recently from 88f57da to 1a16dbd Compare August 13, 2026 21:30
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch 3 times, most recently from 5ed738b to eec713b Compare August 14, 2026 08:42
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch 2 times, most recently from 4d1acce to e0f6267 Compare August 14, 2026 09:18
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch 5 times, most recently from 5acf494 to 3d448aa Compare August 18, 2026 10:10
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch 10 times, most recently from 1b0e5f0 to 3e58b01 Compare August 23, 2026 08:04
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch from 375485d to c74639a Compare August 24, 2026 06:36
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch from c74639a to 9e22500 Compare August 24, 2026 06:37
@charming-wicket-5502

Copy link
Copy Markdown
Contributor

Bug: Public CRL distribution point is not primed by certificate revocation

PR / branch: #987 · fix/put_kms_public_url_in_allowed_cors_by_default @ 9e225007
Component: server — CRL (routes/crl.rs, core/operations/generate_crl.rs, core/operations/revoke.rs)
Severity: Medium (feature is effectively non-functional via the intended public path; workaround exists)

Summary

The public CRL endpoint GET /public/certificates/{issuer_id}/crl only serves a CRL
that has already been built and cached (in-memory GENERATED_CRL_CACHE + crls DB table).
That cache is supposed to be populated automatically when a certificate is revoked
(revoke.rstrigger_crl_regenerationgenerate_crl). In testing, revoking a
valid CA-issued certificate did not make the public endpoint serve a CRL — it kept
returning 404. The public CRL only became available after an authenticated
GET /certificates/{issuer_id}/crl call was made (which runs generate_crl and primes
the cache). So the "automatic" distribution point requires a manual authenticated
generate first, defeating its purpose.

Steps to reproduce

  1. Start KMS with kms_public_url set (config-only CryptoOfficer, mTLS).
  2. As CO: Certify a self-signed CA (CA_ID), then Certify a leaf issued by the CA
    (Issuer Certificate Id and Issuer Private Key Id both set → CertificateIssuerCn = "Demo Root CA").
  3. As CO: Revoke the leaf (reason KeyCompromise). Confirm leaf State = Compromised.
  4. Call the public endpoint (do NOT call the authenticated one first):
    GET /public/certificates/CA_ID/crl

Expected vs actual

  • Expected: step 4 returns HTTP 200 with the signed CRL listing the revoked serial.
  • Actual: step 4 returns 404. It only returns 200 after an authenticated
    GET /certificates/CA_ID/crl is invoked once.

Evidence

  • After manually calling the authenticated endpoint, both endpoints returned HTTP 200,
    a valid 719-byte DER CRL (Issuer: CN=Demo Root CA, revoked serial
    768901E4… with reason Key Compromise) — so generate_crl and the CA cert are fine.
  • The revoke code reads the cert's CertificateLink and calls
    trigger_crl_regeneration(issuer_id), but the public cache was still empty post-revoke.

Likely root cause

The auto-trigger on revoke (revoke.rs ~L190–214 / trigger_crl_regeneration) either does
not fire or its generate_crl call fails silently (it's fire-and-forget and logs only a
warn!), so nothing is cached/persisted. The public endpoint then has nothing to serve
and returns a bare 404 with no diagnostic.

Open verification (one step)

Confirm on a clean DB that step 4 is 404 immediately after revoke and before any
authenticated call
. Tail the server log during revoke and check for the
Auto-CRL: triggered CRL regeneration… line vs a warn! failure — this pins down
whether the trigger fires or errors.

Suggested fixes

  • Ensure the revoke auto-trigger reliably generates + persists the CRL (and surface its
    failure — currently swallowed as warn!).
  • Make the public 404 diagnostic (e.g., log the underlying generate_crl error, or return
    a hint) so this isn't invisible to operators.

Related finding (separate ticket)

Certify silently self-signs a certificate when Issuer Certificate Id is set but
Issuer Private Key Id is omitted, while still stamping the CA CertificateLink. This
produces a cert whose KMIP link says "issued by CA" but whose X.509 issuer is itself
(CertificateIssuerCn = <own subject>). Certify should require the issuer key when an
issuer cert is given, or not record the CA link on a self-signed cert.

Base automatically changed from feat/split_key to develop August 25, 2026 21:56
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch from 9e22500 to 98aa85f Compare August 29, 2026 08:43
@Manuthor
Manuthor force-pushed the fix/put_kms_public_url_in_allowed_cors_by_default branch from 98aa85f to b62c3bb Compare August 29, 2026 08:59
@serene-kitfisto-8899 serene-kitfisto-8899 self-assigned this Sep 1, 2026
@serene-kitfisto-8899 serene-kitfisto-8899 removed their assignment Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants