feat(everything): did a massive dnssec refactor to improve robustness
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package dnssec
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// rootAnchor is one IANA root zone trust anchor: a KSK key tag and the SHA-256
|
||||
// digest of that key. A fetched root KSK is trusted if it matches any anchor.
|
||||
type rootAnchor struct {
|
||||
keyTag uint16
|
||||
digest string // SHA-256, uppercase hex (matches DNSKEY.ToDS output)
|
||||
}
|
||||
|
||||
// rootTrustAnchors holds the currently-valid root KSK trust anchors, as
|
||||
// published by IANA (root-anchors.xml). Both the KSK-2017 and KSK-2024 keys
|
||||
// are listed so validation keeps working across the KSK rollover, during which
|
||||
// the root publishes and may sign with either key. The expired 2010 anchor
|
||||
// (key tag 19036) is intentionally omitted.
|
||||
var rootTrustAnchors = []rootAnchor{
|
||||
{keyTag: 20326, digest: "E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"}, // KSK-2017
|
||||
{keyTag: 38696, digest: "683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16"}, // KSK-2024
|
||||
}
|
||||
|
||||
const rootDigestType uint8 = dns.SHA256
|
||||
|
||||
// rootHints holds the IPv4 addresses of the DNS root servers, used to
|
||||
// bootstrap iterative resolution in chain-of-trust (authoritative) mode.
|
||||
var rootHints = []string{
|
||||
// Root servers. {a..m}.root-servers.net
|
||||
"198.41.0.4:53",
|
||||
"170.247.170.2:53",
|
||||
"192.33.4.12:53",
|
||||
"199.7.91.13:53",
|
||||
"192.203.230.10:53",
|
||||
"192.5.5.241:53",
|
||||
"192.112.36.4:53",
|
||||
"198.97.190.53:53",
|
||||
"192.36.148.17:53",
|
||||
"192.58.128.30:53",
|
||||
"193.0.14.129:53",
|
||||
"199.7.83.42:53",
|
||||
"202.12.27.33:53",
|
||||
}
|
||||
|
||||
|
||||
// matchesRootAnchor reports whether a root DNSKEY matches any trusted anchor,
|
||||
// by key tag and SHA-256 digest.
|
||||
func matchesRootAnchor(k *dns.DNSKEY) bool {
|
||||
tag := k.KeyTag()
|
||||
for _, a := range rootTrustAnchors {
|
||||
if tag != a.keyTag {
|
||||
continue
|
||||
}
|
||||
ds := k.ToDS(rootDigestType)
|
||||
if ds != nil && strings.EqualFold(ds.Digest, a.digest) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// verifyRootAnchor confirms that a fetched root DNSKEY RRset contains a KSK
|
||||
// matching one of the hardcoded trust anchors, and that the RRset is signed by
|
||||
// a key in the set. On success the returned zone can validate child DS records.
|
||||
func verifyRootAnchor(dnskeys *RRSet) (*SignedZone, error) {
|
||||
if dnskeys == nil || dnskeys.IsEmpty() || !dnskeys.IsSigned() {
|
||||
return nil, ErrDnskeyNotAvailable
|
||||
}
|
||||
|
||||
zone := NewSignedZone(".")
|
||||
zone.DNSKey = dnskeys
|
||||
|
||||
matched := false
|
||||
for _, rr := range dnskeys.RRs {
|
||||
k, ok := rr.(*dns.DNSKEY)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
zone.AddPubKey(k)
|
||||
if matchesRootAnchor(k) {
|
||||
matched = true
|
||||
}
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return nil, ErrDsInvalid
|
||||
}
|
||||
|
||||
// The DNSKEY RRset must be signed by one of its own keys (the KSK).
|
||||
if err := zone.VerifyRRSIG(dnskeys); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return zone, nil
|
||||
}
|
||||
Reference in New Issue
Block a user