step certificate lint: a --profile flag, and retiring the zlint/zcrypto forks

Aperta
#1,723 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
32/100
Tipo di issue
Funzionalità
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
go

Direzione di ricerca

Review the linked branch and the certificate lint implementation, especially lint.go and inspect.go, then check the zmap/zlint v3 registry and zcrypto API changes described in the issue. Confirm the profile filtering, Go version requirement, and provisioner JSON compatibility with the maintainers. Done means agreeing on the design and compatibility behavior before opening a pull request; the issue notes that a local implementation already exists.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

enhancement needs triage

Hello!

  • Vote on this issue by adding a 👍 reaction
  • If you want to implement this feature, comment to let us know (we'll work with you on design, scheduling, etc.)

Issue details

I'm looking into packaging step-ca and step for Debian. Debian is unhappy
with vendored forks of libraries it already ships, so I spent some time on
smallstep/zlint and smallstep/zcrypto (both untouched for years) to
work out what they are for and whether they are still needed.

As far as I can tell:

  • zlint its only functional change is one line, from 2018: ED25519SIG
    added to lint_signature_algorithm_not_supported, so Ed25519 certs don't
    fail.
    My reading: step is mostly used for internal PKI, and Ed25519 is
    fine there.
  • zcrypto carries Ed25519/X25519 support and the step_provisioner
    extension.

Two of those three reasons have since evaporated: zmap/zcrypto has had
Ed25519 and X25519 for a while, and it now parses the provisioner extension
too (https://github.com/zmap/zcrypto/pull/545, merged 2026-10-21 — I sent it
while looking into this). That leaves the zlint line.

A different way to do it

A possible solution, which I have implemented and tested locally (link below),
is a new flag:

    step certificate lint --profile {private,webpki}
  • private, the default, applies only the lint families that make sense for
    an internal PKI: everything except the CA/Browser Forum and root-store
    families
  • webpki applies everything, including the CA/Browser Forum and root-store
    families, for certificates meant to be publicly trusted

Behind the scenes

  1. zlint v3 (more on that below) tags every lint with a source (RFC5280,
    CABF_BR, CABF_EV, Mozilla, Apple, ETSI_ESI, Community, …), and
  2. LintCertificateEx takes a filtered lint.Registry.

Improvements The smallstep-patched lint is a CABF Baseline Requirements
lint, so relaxing it makes step certificate lint report BR compliance
for a certificate that isn't BR-compliant, including when someone is
deliberately checking a public certificate!

Filtering via --profile means that an internal cert will pass (because CABF
lints weren't applied). As a result, the CABF signature-algorithm lint can
stay correct.

I tested this on a self-signed Ed25519 CA certificate:

today               231 lints   2 errors
--profile=private   150 lints   0 errors
--profile=webpki    338 lints   4 errors (incl. the Ed25519 ones)

Ed25519 still passes by default, so nobody suddenly starts seeing failures.
The output does change though: the default profile reports 150 lints rather
than 231, so anything parsing this will see fewer entries.

As a bonus, the two errors reported today (smallstep forks)

    e_ca_key_usage_missing
    e_root_ca_key_usage_present

also go away under the default (private) profile. They are BR-only too, and
don't apply to a private CA.

Implementation - A few changes needed

The catch is that the flag can't be built on top of the current
smallstep/zlint. Filtering lints by source is a zlint v3 feature, and the
fork was created before v3. smallstep/zlint has no lint.Registry,
no FilterOptions and no LintCertificateEx.
Its ResultSet simply runs every registered lint, with no way to select a
subset. So, the flag means moving to zmap/zlint/v3.

That pulls zcrypto along with it. zlint v3's LintCertificate takes a
zmap/zcrypto/x509.Certificate, so lint.go has to parse with zmap's
zcrypto rather than smallstep's. inspect.go is the only other file using
that type, and keeping it on the fork would mean compiling two near-identical
x509 packages into the same binary. My take: it would be best if it gets
removed as well.

Result: My suggestion would then be that both smallstep forks go.
This is, of course, what I was after (from the Debian packaging perspective).
But, as far as I can tell (and I might be missing something!) it actually
improves things anyway, now that we are a couple of years after the smallstep
forks were created:

  • Ed25519 and X25519 are in zmap/zcrypto already
  • The provisioner extension is too, because of the PR mentioned above
  • The Ed25519 lint behaviour is what --profile=private restores, but now
    more correctly?

I found two things that smallstep/zcrypto has, that upstream doesn't:

  1. root_js.go, a wasm build stub (not sure what uses it?), and
  2. extended_key_usage_gen.go, a code generator, with a hardcoded URL
    pointed at smallstep/constants instead of zmap/constants

I found the following while implementing this:

  • Go 1.27. zcrypto increased the required go version to 1.27.0 on
    2026-10-15 (7e1570b, PQC support in tls), so importing zcrypto requires
    at least that.

  • The provisioner JSON keys change. I followed zcrypto's snake_case
    rather than copying smallstep's:

    before  {"credentialID":"cred-1","env":"prod","name":"acme","type":"ACME"}
    after   {"type":"ACME","name":"acme","credential_id":"cred-1",
             "key_value_pairs":{"env":"prod"}}
    

    Nesting the pairs stops a (theoretical) metadata key called type
    overwriting the real one, but it is still a breaking change for anyone
    parsing this
    . I didn't realise this difference until after the PR had been
    merged (I followed a previous addition to their store to see how it needs to
    be done, and used that as an example to build my PR on). If you prefer it,
    I am sure that the PR can be amended to follow the smallstep key names.

Check it out

The branch is here if you want a look before deciding whether it is worth a
PR:

https://github.com/smallstep/cli/compare/master...edmundlod:smallstep-cli:lint-profile

The code changes are rather small and few. The high line count comes from
go.sum mostly.

go build ./... and go test ./command/certificate/ both pass. I also
checked that step certificate inspect --format json still renders
step_provisioner as a named object, against a certificate I generated
carrying that OID, and that an invalid --profile value exits 1 with a
sensible message.

If this all looks fine to you, I would be very happy to open a proper PR. I
just thought I would first run it by you.

If you think this is not at all what you want, please let me know as well.
That is still useful to know. Happy to make any changes you want as well.

Why is this needed?

  1. For step users. step certificate lint currently applies CA/Browser
    Forum rules to every certificate, including the internal ones step-ca
    issues. That produces errors that don't apply (e_ca_key_usage_missing,
    e_root_ca_key_usage_present above), and in the other direction it
    reports BR compliance for certificates that aren't BR-compliant.
    A profile fixes both ends, and the default keeps today's behaviour
    for Ed25519.

  2. For the project. It retires two forks that haven't had a functional
    change since 2018, in favour of an actively maintained upstream.
    smallstep/cli is the only smallstep repo that depends on them.
    certificates, crypto and certinfo have no zcrypto or zlint in their go.mod.

  3. For packagers. Debian doesn't like shipping vendored forks of libraries
    already in the archive, so today step cannot easily be packaged there.

Lingua principale
Go
Stelle
4.3k
Fork
320
Merge medio
2g 3h
PR unite (30g)
11

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di smallstep/cli

Tutte le issue di smallstep/cli

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.