Check the S/MIME half against a real server, instead of assuming it

The section offered "an OpenPGP public key or an S/MIME certificate" and
only the first half had ever been tried. Every probe behind it used
OpenPGP keys, and every message the registry returns names OpenPGP --
including for input that is not OpenPGP at all -- so the server reads as
though OpenPGP were the only format it knows. Shipping the claim on that
evidence would have been a guess dressed as a feature, which is the one
thing this section is written not to do.

It holds. Confirmed live on 0.16.20 (2026-09-05) with a self-signed
X.509 certificate carrying emailProtection and an email: SAN:
registered, read back, destroyed. And Stalwart parses it as seriously as
it parses OpenPGP -- a malformed certificate is refused by a decoder of
its own, "Failed to decode X509 certificate: BER decoding error:
Expected Tag { class: Universal, value: 16 } tag…", which is a third
rejection wording and the reason the S/MIME half is real rather than
decorative. The mock now returns it for a certificate, so the branch
exists somewhere a client can meet it.

One thing found on the way: expiresAt is the registry's field and is not
derived from the key. A certificate valid for a year registers with
expiresAt null, so the card says "No expiry set" about a credential that
does expire. Left as it is, deliberately: reading the real date means
parsing the certificate, which is the second opinion this section
refuses to offer, and a date extracted here would disagree with the
server's own field the moment the two ever differed. What the row
reports is what the registry holds, and KNOWN-ISSUES says so.
This commit is contained in:
2026-09-05 01:03:52 -07:00
parent e93d42d27e
commit 6a467d9bc4
3 changed files with 30 additions and 11 deletions
+11
View File
@@ -1236,6 +1236,12 @@ first two:
checked would be a second opinion, and the one that counts would still be the
server's — its message is shown verbatim, the way password-policy rejections
already are.
- **Certificates are parsed too, by their own decoder.** The registry is not
OpenPGP-only, though every other message it returns reads as though it were: a
self-signed X.509 certificate registers, reads back and removes cleanly, and a
malformed one is refused with *"Failed to decode X509 certificate: BER
decoding error…"*. So the S/MIME half of this section is real, and the server
is the thing checking it in both formats.
- **A key that parses can still be refused, and says something else.** A
sign-and-certify key with no encryption subkey — which is what
`gpg --quick-generate-key` produces by default — comes back *"Could not find
@@ -1252,6 +1258,11 @@ first two:
reloads the list instead of believing what came back. Renaming and removing
both work; removal leaves the registry genuinely empty.
**`expiresAt` is the registry's field, not the key's.** A certificate valid for
a year registers with no expiry set, and the card says so. Reading the real one
means parsing the certificate, which is the second opinion this section exists
not to offer.
The kind badge — OpenPGP or S/MIME — is read from the armour header alone, which
is a label rather than a parse, and the excerpt beside each key is deliberately
not called a fingerprint: computing a real one means parsing the key, and naming