-
Notifications
You must be signed in to change notification settings - Fork 4
/
authchain.go
107 lines (92 loc) · 3.18 KB
/
authchain.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package goresolver
import (
"github.com/miekg/dns"
"log"
"strings"
)
// AuthenticationChain represents the DNSSEC chain of trust from the
// queried zone to the root (.) zone. In order for a zone to validate,
// it is required that each zone in the chain validate against its
// parent using the DS record.
//
// https://www.ietf.org/rfc/rfc4033.txt
type AuthenticationChain struct {
delegationChain []SignedZone
}
// Populate queries the RRs required for the zone validation
// It begins the queries at the *domainName* zone and then walks
// up the delegation tree all the way up to the root zone, thus
// populating a linked list of SignedZone objects.
func (authChain *AuthenticationChain) Populate(domainName string) error {
qnameComponents := strings.Split(domainName, ".")
zonesToVerify := len(qnameComponents)
// TODO add test case
if zonesToVerify < 0 {
zonesToVerify = 0
}
authChain.delegationChain = make([]SignedZone, 0, zonesToVerify)
for i := 0; i < zonesToVerify; i++ {
zoneName := dns.Fqdn(strings.Join(qnameComponents[i:], "."))
delegation, err := queryDelegation(zoneName)
if err != nil {
return err
}
if i > 0 {
authChain.delegationChain[i-1].parentZone = delegation
}
authChain.delegationChain = append(authChain.delegationChain, *delegation)
}
return nil
}
// Verify uses the zone data in delegationChain to validate the DNSSEC
// chain of trust.
// It starts the verification in the RRSet supplied as parameter (verifies
// the RRSIG on the answer RRs), and, assuming a signature is correct and
// valid, it walks through the delegationChain checking the RRSIGs on
// the DNSKEY and DS resource record sets, as well as correctness of each
// delegation using the lower level methods in SignedZone.
func (authChain *AuthenticationChain) Verify(answerRRset *RRSet) error {
signedZone := authChain.delegationChain[0]
if !signedZone.checkHasDnskeys() {
return ErrDnskeyNotAvailable
}
err := signedZone.verifyRRSIG(answerRRset)
if err != nil {
log.Println("RRSIG didn't verify", err)
return ErrInvalidRRsig
}
for _, signedZone := range authChain.delegationChain {
if signedZone.dnskey.IsEmpty() {
log.Printf("DNSKEY RR does not exist on %s\n", signedZone.zone)
return ErrDnskeyNotAvailable
}
// Verify the RRSIG of the DNSKEY RRset with the public KSK.
err := signedZone.verifyRRSIG(signedZone.dnskey)
if err != nil {
log.Printf("validation DNSKEY: %s\n", err)
return ErrRrsigValidationError
}
if signedZone.parentZone != nil {
if signedZone.ds.IsEmpty() {
log.Printf("DS RR is not available on zoneName %s\n", signedZone.zone)
return ErrDsNotAvailable
}
err := signedZone.parentZone.verifyRRSIG(signedZone.ds)
if err != nil {
log.Printf("DS on %s doesn't validate against RRSIG %d\n", signedZone.zone, signedZone.ds.rrSig.KeyTag)
return ErrRrsigValidationError
}
err = signedZone.verifyDS(signedZone.ds.rrSet)
if err != nil {
log.Printf("DS does not validate: %s", err)
return ErrDsInvalid
}
}
}
return nil
}
// NewAuthenticationChain initializes an AuthenticationChain object and
// returns a reference to it.
func NewAuthenticationChain() *AuthenticationChain {
return &AuthenticationChain{}
}