dnssec

package
v1.6.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package dnssec implements pure DNSSEC verification primitives: RRSIG/DS validation, NSEC and NSEC3 denial-of-existence proofs, and the EDE-coded sentinel errors they return. None of the functions here hold resolver state; they take the records they need to validate as inputs and return a pass/fail result. The recursive resolver wraps them with the chain-of-trust orchestration (DS lookups, key fetches, trust-anchor management).

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNoDNSKEY = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSKEYMissing,
		Message: "No DNSKEY records found in response",
	}
	ErrMissingKSK = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSKEYMissing,
		Message: "No KSK DNSKEY matches DS records from parent",
	}
	ErrFailedToConvertKSK = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSBogus,
		Message: "Unable to validate DNSKEY against parent DS record",
	}
	ErrMismatchingDS = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSBogus,
		Message: "DNSKEY does not match DS record from parent zone",
	}
	ErrNoSignatures = &util.EDEError{
		Code:    dns.ExtendedErrorCodeRRSIGsMissing,
		Message: "Response is missing required RRSIG records",
	}
	ErrMissingDNSKEY = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSKEYMissing,
		Message: "No DNSKEY found to validate RRSIG",
	}
	ErrInvalidSignaturePeriod = &util.EDEError{
		Code:    dns.ExtendedErrorCodeSignatureExpired,
		Message: "RRSIG validity period check failed",
	}
	ErrMissingSigned = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSBogus,
		Message: "RRsets covered by RRSIG are missing",
	}
	ErrDSRecords = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSBogus,
		Message: "Parent has DS records but zone appears unsigned",
	}
	ErrTrustAnchorsUnavailable = &util.EDEError{
		Code:    dns.ExtendedErrorCodeOther,
		Message: "Trust anchors unavailable — refusing to validate",
	}
)

DNSKEY-side validation errors.

View Source
var (
	ErrNSECTypeExists = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSBogus,
		Message: "NSEC record indicates queried type exists",
	}
	ErrNSECMissingCoverage = &util.EDEError{
		Code:    dns.ExtendedErrorCodeNSECMissing,
		Message: "Incomplete NSEC proof for name non-existence",
	}
	ErrNSECBadDelegation = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSBogus,
		Message: "Invalid NSEC type bitmap for delegation",
	}
	ErrNSECNSMissing = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSBogus,
		Message: "NSEC missing NS bit at delegation point",
	}
	ErrNSECOptOut = &util.EDEError{
		Code:    dns.ExtendedErrorCodeDNSBogus,
		Message: "NSEC3 opt-out validation failed",
	}
)

NSEC / NSEC3 denial-of-existence errors.

Functions

func DNSKEYMissingForZone

func DNSKEYMissingForZone(zone string) *util.EDEError

DNSKEYMissingForZone returns a DNSKEY-missing error tagged with zone.

func IsSupportedDNSKEYAlgorithm

func IsSupportedDNSKEYAlgorithm(alg uint8) bool

IsSupportedDNSKEYAlgorithm reports whether miekg/dns' RRSIG.Verify can process signatures of the given algorithm without returning ErrAlg. DS records advertising unsupported DNSKEY algorithms are unusable — DNSKEY.ToDS will still hash them, but later RRSIG verification would fail. Per RFC 6840 §5.2 such DS entries must be disregarded so an unsupported-only DS RRset is treated as insecure rather than bogus.

The list intentionally matches miekg/dns' switch in RRSIG.Verify exactly. RSAMD5 (deprecated by RFC 8624) is *not* accepted there, so classifying it as supported would let an RSAMD5 DS RRset appear usable and then bogus out on verification instead of downgrading to insecure.

func IsSupportedDS

func IsSupportedDS(ds *dns.DS) bool

IsSupportedDS reports whether a DS record is usable for validation: both its digest type and the DNSKEY algorithm it advertises must be something this validator can verify.

func IsSupportedDSDigest

func IsSupportedDSDigest(t uint8) bool

IsSupportedDSDigest reports whether the given DS digest type is implemented locally. RFC 6840 §5.2 requires validators to ignore DS records that use unknown or unimplemented digest algorithms. Only the three digest types miekg/dns' DNSKEY.ToDS actually computes are treated as supported here — anything else (GOST94, future digest types, unknown values) is skipped.

func SignatureExpiredForRRset

func SignatureExpiredForRRset(rrtype, zone string) *util.EDEError

SignatureExpiredForRRset returns a signature-expired error tagged with the RR type and zone.

func ValidateSigner

func ValidateSigner(signer, qname string) error

ValidateSigner checks that the signer claimed by an RRSIG is a plausible zone apex for qname — either qname itself or a proper ancestor. The check must run before the DS-chain lookup because RRSIG.SignerName is unauthenticated RDATA until a key verifies the signature: without it, an on-path attacker can rewrite SignerName to an unsigned sibling or descendant, then rely on findDS() returning an empty set to silently skip verifyDNSSEC() and downgrade a signed response to "insecure" instead of bogus.

func VerifyDS

func VerifyDS(keyMap map[uint16][]*dns.DNSKEY, parentDSSet []dns.RR) (bool, error)

VerifyDS looks for a DS record in parentDSSet that authenticates one of the KSKs in keyMap. It returns (unsupportedOnly, err):

  • (false, nil) — at least one supported DS matched a KSK.
  • (false, err) — at least one supported DS was present but none matched; the zone is bogus (not "insecure").
  • (true, err) — every DS in the RRset uses an unsupported digest type. Per RFC 6840 §5.2 validators MUST ignore such records, and if none remain the caller must treat the zone as insecure.

keyMap groups DNSKEYs by key tag because RFC 4034 Appendix B.1 does not guarantee key-tag uniqueness: a colliding tag could otherwise mask the KSK that actually authenticates the DS.

func VerifyDelegation

func VerifyDelegation(delegation string, nsec []dns.RR) error

VerifyDelegation verifies an insecure-delegation claim using NSEC3. The delegation is authenticated either by an exact-match NSEC3 with NS set (and DS / SOA cleared) or, for opt-out spans, by an NSEC3 covering the next closer name with the Opt-Out bit set.

func VerifyDelegationNSEC

func VerifyDelegationNSEC(delegation string, nsecSet []dns.RR) error

VerifyDelegationNSEC verifies an insecure-delegation claim using NSEC records (RFC 4035 §5.2). It must find an NSEC whose owner equals the delegation name and whose type bitmap contains NS but neither DS nor SOA. Anything looser would let a malicious parent strip the DS from a signed child and have the resolver treat the child as insecure.

func VerifyNODATA

func VerifyNODATA(msg *dns.Msg, nsec []dns.RR) error

VerifyNODATA verifies a NODATA proof using NSEC3 records (RFC 5155 §8.5–§8.7), including the DS-specific opt-out branch.

func VerifyNODATANSEC

func VerifyNODATANSEC(msg *dns.Msg, nsecSet []dns.RR) error

VerifyNODATANSEC verifies NODATA using NSEC records (RFC 4035 §3.1.3.1).

func VerifyNSEC

func VerifyNSEC(q dns.Question, nsecSet []dns.RR) (typeMatch bool)

VerifyNSEC reports whether any NSEC in nsecSet has q.Qtype set in its type bitmap. This is a cheap structural check used as a pre-filter before the full denial-of-existence proofs in VerifyNODATANSEC / VerifyNameErrorNSEC. A true result alone does not authenticate anything.

func VerifyNameError

func VerifyNameError(msg *dns.Msg, nsec []dns.RR) error

VerifyNameError verifies an NXDOMAIN proof using NSEC3 records (RFC 5155 §8.4): closest encloser exists, an NSEC3 covers the next closer name, and an NSEC3 covers the wildcard at the closest encloser.

func VerifyNameErrorNSEC

func VerifyNameErrorNSEC(msg *dns.Msg, nsecSet []dns.RR) error

VerifyNameErrorNSEC verifies NXDOMAIN using NSEC records (RFC 4035 §3.1.3.2). The proof requires two NSEC records: one that covers QNAME (proving QNAME does not exist) and one that covers the wildcard at the closest encloser of QNAME (proving no wildcard match could synthesize the answer). Accepting wildcard coverage from an arbitrary ancestor lets a mismatched proof pass, so the wildcard is derived from the covering NSEC's owner/next labels, not from any ancestor of QNAME.

func VerifyRRSIG

func VerifyRRSIG(signer string, keys map[uint16][]*dns.DNSKEY, msg *dns.Msg) (bool, error)

VerifyRRSIG validates that every in-zone RRset in msg is covered by at least one RRSIG that successfully verifies against the supplied DNSKEYs.

The signer zone is supplied by the caller (verifyDNSSEC / verifyRootKeys) and represents the zone whose keys should authenticate this response. RRsets whose owner name is not in that zone — for example, target records appended via DNAME synthesis — are validated by their own recursion and are skipped here. An unsigned RRset inside the zone is rejected; a signature that fails (missing key, bad exponent, verify error, expired) only causes the RRset to fail if no sibling signature succeeds.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL