Skip to main content
Guides/DNS Records

DNSSEC cryptographic verification: what we actually verify

DNSSEC is supposed to give you a cryptographic guarantee that a DNS answer is authentic. Most tools that claim to "check DNSSEC" don't actually verify any cryptography — they trust the recursive resolver's AD bit and call it a day. EdgeDNS reconstructs the canonical signed RRset and runs the math locally. Here's what that means, why the difference matters, and how to read what comes back.

EdgeDNS Team··11 min read

The problem DNSSEC was built to solve

The original DNS (Domain Name System) specification from 1983 had no way to verify that an answer was real. A resolver got back an IP address, trusted it, and used it. An attacker who could intercept or poison DNS responses on a network — your home WiFi, a coffee-shop network, an ISP's caching layer — could redirect users to fake versions of any website without setting off any alarms. The technical name for the attack is DNS spoofing or cache poisoning, and it remains one of the most dangerous attacks against any business that handles money, credentials, or sensitive data online.

DNSSEC (Domain Name System Security Extensions) is the IETF's answer. Instead of asking resolvers to trust answers blindly, DNSSEC has the zone operator publish cryptographic signatures alongside their records. Resolvers verify the signatures against public keys that are themselves authenticated up a chain of trust — child zone → parent zone → root zone — that ends at the well-known IANA root trust anchor. When a poisoned answer arrives, the signature won't match the public key, and the resolver discards the response. The full set of standards lives in RFC 4033, RFC 4034, and RFC 4035.

In practice, every major recursive resolver — Cloudflare 1.1.1.1, Google 8.8.8.8, Quad9 — validates DNSSEC by default. Which means the question for any operator is no longer "is DNSSEC worth it?" — it's "is my zone correctly signed, or am I about to have a self-inflicted outage when the validators reject my data?"

Why "the AD bit said yes" is not verification

When a recursive resolver successfully validates a DNSSEC signature, it sets a flag in its reply called AD (Authenticated Data). Most DNSSEC-checking tools query a recursive resolver, read the AD bit out of the response, and report "DNSSEC: secure ✓" if it's set. Done.

The problem with this approach is that the AD bit tells you what some other software said about your zone, not what the math actually shows. If Cloudflare's resolver has cached an old answer from before your KSK rolled, the AD bit may still be set on stale data. If a resolver doesn't validate at all, it returns AD=0 even on perfectly signed zones. If you're running on a network whose resolver has its own validation bugs, the AD bit reflects those bugs, not your zone's reality. The AD bit is a useful third-party indicator, but it is not the same as cryptographic verification.

What you actually want, when something looks wrong, is the math: take the published DS record, take the published DNSKEY, compute the digest, compare. Take the published RRSIG, take the public key, reconstruct the canonical signed data, run the verification primitive. If the math says yes, the chain is sound. If the math says no, you have a concrete failure with a concrete cause.

EdgeDNS does the math.

How verification actually works

DNSSEC verification is a sequence of cryptographic operations layered on top of normal DNS:

1. The zone owner generates a key pair — typically a Key Signing Key (KSK) and a Zone Signing Key (ZSK) — and publishes the public halves as DNSKEY records at the apex of their zone. 2. For every set of records of the same type at the same name (an RRset), the zone signs the canonical wire-format encoding of the RRset with the ZSK and publishes the result as an RRSIG record next to it. 3. The DNSKEY RRset itself is signed by the KSK, producing an RRSIG that authenticates the keys. 4. The zone owner submits a DS (Delegation Signer) record to their parent zone — usually via the registrar's interface. The DS contains a hash of the KSK\'s DNSKEY data. 5. The parent zone signs the DS RRset with its own ZSK, and the chain continues all the way up to the root.

A validator verifying any answer walks this chain in reverse: it grabs the answer's RRSIG, finds the matching DNSKEY (the signing ZSK), checks that DNSKEY is itself signed by a DNSKEY whose hash matches the DS in the parent, checks the parent's DS RRSIG against its parent's DNSKEY, and so on until it reaches a key it already trusts (the root trust anchor).

Most DNSSEC operations are mechanical — the same algorithms applied at each level. The interesting parts are the two cryptographic checks at each hop: DS-digest verification (does the parent's DS actually correspond to the child's DNSKEY?) and RRSIG signature verification (does the signature actually verify against the public key?). Those are what we run.

The three layers we verify

When you call `/v1/dns/dnssec`, three layers of verification run in order:

Layer 1: structural. Are the right records present? DNSKEY at the apex, DS at the parent, RRSIG covering the DNSKEY RRset. Are key tags consistent across DS and DNSKEY? Are algorithms compatible? Are there any revoked keys (the RFC 5011 revoke bit)? Any orphaned RRSIGs that reference non-existent DNSKEYs? Structural problems are surfaced as `issues` even when the math itself works.

Layer 2: DS digest. For each published DS, find the candidate KSK DNSKEY (matching key tag and algorithm), build the canonical input — the lowercased owner name in DNS wire format, followed by the DNSKEY RDATA bytes — and compute SHA-1, SHA-256, or SHA-384 depending on the DS digest type. Compare the result to the digest in the DS record. The structure of this calculation is specified in RFC 4034 §5.1.4. The response surfaces every check in `ds_digest_checks[]` with `digest_computed`, `ds_digest_published`, and a `matches` boolean.

Layer 3: RRSIG signature. Reconstruct the canonical signed data — RRSIG RDATA prefix (without the signature itself) followed by the canonical-form RRset, sorted lexicographically per RFC 4034 §6.2 — then verify the signature against the matching DNSKEY's public key using the algorithm in the RRSIG header. RSA keys are decoded from the RFC 3110 wire format and imported as JWK; ECDSA and Ed25519 keys are imported as raw points. Verification calls the standard Web Crypto `verify()` primitive. Today, the endpoint runs this on the DNSKEY RRset's self-signature — the most tractable check that doesn't require additional queries — and reports each result in `rrsig_signature_checks[]`.

A failure in layer 2 or 3 marks the zone as `bogus` and downgrades `has_valid_chain` to false. Validators behave the same way: a bogus zone returns SERVFAIL to every client.

The most common DNSSEC failure in the wild

Across operational DNSSEC deployments, one specific failure mode is responsible for the majority of self-inflicted outages: KSK rolled, parent DS not updated.

Here's what happens. The zone operator rotates their Key Signing Key — a routine security operation that should happen periodically. The new DNSKEY is published. The zone re-signs everything with the new keys. Internally, everything looks fine. But the DS record at the registrar still references the old KSK. From a validating resolver's point of view, the parent zone says "trust the key with this hash," and the child zone publishes a totally different key. The math fails. The zone goes bogus. Validating resolvers SERVFAIL. Users on Cloudflare/Google/Quad9 stop being able to reach the domain.

This is a binary outage with a single root cause and a single fix: republish the correct DS at the registrar. But to find that root cause, you have to actually compare digests — and that's exactly what most DNSSEC-checking tools don't do.

A call to `/v1/dns/dnssec` for a broken zone returns:

  • `status`: `"bogus"`

  • `ds_digest_verified`: `false`

  • `ds_digest_checks[].matches`: `false` for the DS that doesn't correspond to any current DNSKEY

  • `ds_digest_checks[].digest_computed`: the digest your zone should be publishing

  • `recommendations`: an actionable string telling you to update the DS at the registrar with the new digest

That's the difference between "your DNSSEC might be broken" and "publish this exact DS digest at your registrar to fix it."

Reading the response

A healthy zone returns:

  • `status: "secure"`

  • `has_dnssec: true` and `has_valid_chain: true`

  • `ds_digest_verified: true` (at least one DS matched a DNSKEY)

  • `rrsig_signatures_verified: true` (the DNSKEY self-signature verified)

  • `keys[]` listing each DNSKEY with `key_tag`, `algorithm`, `is_ksk`, `is_zsk`, `is_secure`

  • `ds_records[]` listing each DS with `key_tag`, `algorithm`, `digest_type`

  • `rrsig_records[]` listing each RRSIG with `expiration`, `inception`, `is_expired`, `is_expiring_soon`

  • `ds_digest_checks[]` and `rrsig_signature_checks[]` with the per-check detail

A bogus zone keeps the same shape but with `status: "bogus"`, `has_valid_chain: false`, and at least one entry in `ds_digest_checks` or `rrsig_signature_checks` with `matches: false` or `verified: false`. The `issues[]` and `recommendations[]` arrays explain the failure in plain English.

Useful quick checks:

  • `body.data.status === 'secure'` — your zone is verified end-to-end.

  • `body.data.ds_digest_verified === false` — the parent's DS doesn't correspond to any DNSKEY you publish; almost always a key-rollover issue.

  • `body.data.rrsig_records.some(r => r.is_expiring_soon)` — schedule re-signing before validators start rejecting your zone.

bash
curl -sH "Authorization: Bearer $KEY" \
  'https://api.edgedns.dev/v1/dns/dnssec?domain=cloudflare.com' | jq

Which algorithms we support

DNSSEC supports a long history of cryptographic algorithms, but only a few are still in widespread modern use. The endpoint verifies signatures for:

  • RSA-SHA256 (algorithm 8) and RSA-SHA512 (algorithm 10) per RFC 5702. RSA-SHA256 is the dominant algorithm on the public internet today.

  • ECDSA P-256 (algorithm 13) and ECDSA P-384 (algorithm 14) per RFC 6605. ECDSA produces smaller signatures than RSA at equivalent security and is the modern recommendation; the root zone uses ECDSA P-256.

  • Ed25519 (algorithm 15) per RFC 8080. The newest curve, smaller and faster than ECDSA P-256, increasingly used by zones that operate at scale.

For older or less-common algorithms — RSA-SHA1 (5/7), DSA (3), GOST (12), Ed448 (16), and the deprecated insecure ones — the structural checks still run (key tag, algorithm, expiry) but cryptographic verification reports `reason: "Algorithm X not supported"`. The IANA DNSSEC algorithm registry is the canonical list; RFC 8624 summarizes the modern recommendations.

If you're picking algorithms today, ECDSA P-256 (13) or Ed25519 (15) are the right choices.

What this currently covers

The endpoint verifies the DNSKEY RRset's self-signature locally — the apex check that proves your zone's signing keys are valid and correctly self-signed without requiring any extra queries. Combined with DS-digest verification at the parent boundary, this catches the vast majority of real-world DNSSEC misconfigurations: KSK rollovers gone wrong, expired RRSIGs, weak algorithms, revoked keys, mismatched DS digests, orphaned signatures.

What the endpoint does not yet do is walk the entire chain back to the IANA root trust anchor. A truly authoritative validation would fetch the parent zone's DNSKEY, verify its self-signature, fetch the grandparent's DS, verify, and so on all the way up — typically 15+ extra queries per call. Chain-to-root walking is on the roadmap and will be available as a paid-tier feature when it ships. Today, you get the cryptographic check at the most common failure boundary; you trust the chain above that to a validating recursive resolver.

That's an honest scope statement: the math at the apex is real, the chain above it is delegated. For the kinds of operational questions zone operators actually have — "is my new KSK live? are my signatures expiring? did the DS update at the registrar take effect?" — the apex check is what gives you the answer.

Fixing a broken DS

When `ds_digest_verified` is `false`, the response includes `ds_digest_checks[]` entries showing exactly which DS doesn't match. The `recommendations[]` array contains a concrete action — usually, "compute the correct DS using your current KSK and update it at the registrar."

Most registrars accept the DS in one of two formats: pre-computed (you supply key tag, algorithm, digest type, and digest) or DNSKEY format (you supply the DNSKEY data and the registrar computes the digest). The `keys[]` array in the response gives you the key_tag and algorithm; the `recommendations` text gives you the digest you should be publishing. Cross-reference with your registrar's DS-update interface and the chain should re-validate within an hour, depending on TTLs.

For RRSIG expiration issues — `rrsig_records[].is_expiring_soon` set on multiple records — schedule a zone re-sign. Most DNS provider control planes have a "re-sign now" button; for self-hosted BIND or Knot, the standard zone-signing tools handle this directly.

Further reading

Standards:

  • RFC 4033 — Introduction and Requirements

  • RFC 4034 — Resource Records (DNSKEY, RRSIG, DS, NSEC)

  • RFC 4035 — Protocol Modifications (validation semantics)

  • RFC 5702 — RSA-SHA256 / RSA-SHA512

  • RFC 6605 — ECDSA P-256 / P-384

  • RFC 8080 — Ed25519 / Ed448

  • RFC 8624 — Algorithm implementation requirements

  • RFC 9276 — NSEC3 parameter guidance

Registries:

Related EdgeDNS endpoints:

Need Programmatic Access?

Automate domain intelligence with 100+ API endpoints and a free MCP server for AI integration.