6 KiB
checker-openpgpkey
DANE-Email posture checker for happyDomain.
Runs a comprehensive testsuite on a domain's DNS-published OpenPGP key
(OPENPGPKEY, RFC 7929) or S/MIME certificate (SMIMEA,
RFC 8162) and renders an actionable HTML report whose top
block nudges the user toward the fix for the most common failure
scenarios.
This checker binds to the happyDomain services:
abstract.OpenPGP: individual user's PGP key, owner-hashed below._openpgpkey.<zone>.abstract.SMimeCert: user's S/MIME certificate, owner-hashed below._smimecert.<zone>.
Tests run
All findings are tagged by severity (info / warn / crit) so the
rule engine can fold them into a single CheckState.
DNS (both record types)
| Code | Severity | What it catches |
|---|---|---|
dns_query_failed |
crit | The resolver returned an error or did not answer. |
dns_no_record |
crit | The authoritative answer has no record at the expected owner. |
dnssec_not_validated |
crit / warn | The validating resolver did not set AD. RFC 7929/8162 mandate DNSSEC; the severity is configurable via requireDNSSEC. |
dns_record_mismatch |
warn | The record returned by DNS differs from the one declared in the service (typically a stale zone on the authoritative servers). |
owner_hash_mismatch |
crit | Record owner-name first label is not sha256(localpart)[:28]; mail clients will never find it. |
OpenPGP-specific (RFC 7929)
| Code | Severity | What it catches |
|---|---|---|
pgp_parse_error |
crit | Malformed base64 or OpenPGP packet stream. |
pgp_no_entity |
crit | Record decoded but carries no valid entity. |
pgp_primary_revoked |
crit | Primary key has a revocation signature. |
pgp_primary_expired |
crit | Self-signature expired; clients will refuse to encrypt. |
pgp_primary_expiring_soon |
warn | Expires within the certExpiryWarnDays window (default 30). |
pgp_weak_algorithm |
warn | Uses DSA / ElGamal (phase-out). |
pgp_weak_key_size |
crit / warn | RSA below 2048 bits is critical, 2048-3071 is a warn. |
pgp_no_encryption_subkey |
crit | No active key in the entity advertises encryption capability. |
pgp_no_identity |
warn | No self-signed User ID. |
pgp_uid_mismatch |
info | None of the UIDs reference <username@…>. |
pgp_multiple_entities |
warn | Record carries more than one entity (RFC 7929 recommends one). |
pgp_record_too_large |
warn | Raw key > 4 KiB; forces UDP→TCP fallback on every lookup. |
SMIMEA-specific (RFC 8162)
| Code | Severity | What it catches |
|---|---|---|
smimea_bad_usage / _selector / _match_type |
crit | Field outside the allowed range. |
smimea_cert_parse_error |
crit | Hex-encoded blob is not a valid X.509 certificate / SPKI. |
smimea_cert_expired / _not_yet_valid |
crit | notBefore / notAfter gate the current time out. |
smimea_cert_expiring_soon |
warn | Within the certExpiryWarnDays window. |
smimea_no_email_protection_eku |
crit / warn | Missing emailProtection EKU (RFC 8550/8551 agents will reject). |
smimea_missing_key_usage |
warn | Neither digitalSignature nor keyEncipherment key-usage is set. |
smimea_email_mismatch |
info | No email SAN starts with <username>@. |
smimea_weak_signature_algorithm |
crit | MD5 / SHA-1 based signature. |
smimea_weak_key_size |
crit / warn | RSA < 2048 / 3072 bits. |
smimea_self_signed |
info | Self-signed certificate paired with PKIX-EE usage. |
smimea_hash_only |
info | Matching-type 1/2 only carries a digest; certificate can't be inspected. |
Why a bespoke checker instead of a third-party testsuite?
There is no canonical "OPENPGPKEY / SMIMEA testsuite" in Go or as a self-hostable online service:
ldns-dane(NLnet Labs) validates DANE-TLSA and handles SMIMEA only shallowly (it parses the record without deep certificate checks).hokey(Paul Wouters) queries OPENPGPKEY but does not validate the key material.- Online DANE validators (e.g.
dane.sys4.de,has-tls-rpt.com) focus on SMTP DANE-TLSA, not email-identity records.
The heavy lifting here is standard Go parsing:
github.com/ProtonMail/go-crypto/openpgp(maintained fork of the deprecatedgolang.org/x/crypto/openpgp) for OpenPGP packet parsing, UIDs, subkeys, revocations, key-lifetime self-signatures.crypto/x509for SMIMEA certificate parsing, validity window, EKU, key-usage, signature-algorithm and key-size checks.github.com/miekg/dnsfor the DNS+EDNS0+DO query and theADflag read-back used as the DNSSEC-validation signal.
Options
| Id | Type | Default | Description |
|---|---|---|---|
resolver |
string | (system) | Validating resolver to query; comma-separated list accepted. |
certExpiryWarnDays |
number | 30 | Raise an expiring_soon warning within this window. |
requireDNSSEC |
bool | true | When false, missing AD is a warn instead of crit. |
requireEmailProtection |
bool | true | When false, missing emailProtection EKU is a warn instead of crit. |
Auto-filled by the host: domain_name, subdomain, service,
service_type.
Running
# Plugin (loaded by happyDomain at startup)
make plugin
# Standalone HTTP server
make && ./checker-openpgpkey -listen :8080
HTML report
The report renders as a self-contained HTML document intended for
embedding in an <iframe> (the same contract as the other happyDomain
checkers). It is organised as:
- Header: status badge, queried owner name, resolver used, DNSSEC flag.
- Most common issues (fix these first): remediation cards shown only when a matching finding was emitted. Each card carries the concrete shell commands / zone-file snippets the user needs.
- OpenPGP key / SMIMEA record: structured details for the parsed material (fingerprint, UIDs, subkeys, cert subject/issuer, EKU/KU flags, …).
- Findings: the full table of per-finding code / severity / message / fix hints, sorted by severity.