1package dns
2
3import (
4	"crypto"
5	"crypto/dsa"
6	"crypto/ecdsa"
7	"crypto/rsa"
8	"encoding/binary"
9	"math/big"
10	"strings"
11	"time"
12)
13
14// Sign signs a dns.Msg. It fills the signature with the appropriate data.
15// The SIG record should have the SignerName, KeyTag, Algorithm, Inception
16// and Expiration set.
17func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
18	if k == nil {
19		return nil, ErrPrivKey
20	}
21	if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
22		return nil, ErrKey
23	}
24
25	rr.Hdr = RR_Header{Name: ".", Rrtype: TypeSIG, Class: ClassANY, Ttl: 0}
26	rr.OrigTtl, rr.TypeCovered, rr.Labels = 0, 0, 0
27
28	buf := make([]byte, m.Len()+Len(rr))
29	mbuf, err := m.PackBuffer(buf)
30	if err != nil {
31		return nil, err
32	}
33	if &buf[0] != &mbuf[0] {
34		return nil, ErrBuf
35	}
36	off, err := PackRR(rr, buf, len(mbuf), nil, false)
37	if err != nil {
38		return nil, err
39	}
40	buf = buf[:off:cap(buf)]
41
42	hash, ok := AlgorithmToHash[rr.Algorithm]
43	if !ok {
44		return nil, ErrAlg
45	}
46
47	hasher := hash.New()
48	// Write SIG rdata
49	hasher.Write(buf[len(mbuf)+1+2+2+4+2:])
50	// Write message
51	hasher.Write(buf[:len(mbuf)])
52
53	signature, err := sign(k, hasher.Sum(nil), hash, rr.Algorithm)
54	if err != nil {
55		return nil, err
56	}
57
58	rr.Signature = toBase64(signature)
59
60	buf = append(buf, signature...)
61	if len(buf) > int(^uint16(0)) {
62		return nil, ErrBuf
63	}
64	// Adjust sig data length
65	rdoff := len(mbuf) + 1 + 2 + 2 + 4
66	rdlen := binary.BigEndian.Uint16(buf[rdoff:])
67	rdlen += uint16(len(signature))
68	binary.BigEndian.PutUint16(buf[rdoff:], rdlen)
69	// Adjust additional count
70	adc := binary.BigEndian.Uint16(buf[10:])
71	adc++
72	binary.BigEndian.PutUint16(buf[10:], adc)
73	return buf, nil
74}
75
76// Verify validates the message buf using the key k.
77// It's assumed that buf is a valid message from which rr was unpacked.
78func (rr *SIG) Verify(k *KEY, buf []byte) error {
79	if k == nil {
80		return ErrKey
81	}
82	if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
83		return ErrKey
84	}
85
86	var hash crypto.Hash
87	switch rr.Algorithm {
88	case DSA, RSASHA1:
89		hash = crypto.SHA1
90	case RSASHA256, ECDSAP256SHA256:
91		hash = crypto.SHA256
92	case ECDSAP384SHA384:
93		hash = crypto.SHA384
94	case RSASHA512:
95		hash = crypto.SHA512
96	default:
97		return ErrAlg
98	}
99	hasher := hash.New()
100
101	buflen := len(buf)
102	qdc := binary.BigEndian.Uint16(buf[4:])
103	anc := binary.BigEndian.Uint16(buf[6:])
104	auc := binary.BigEndian.Uint16(buf[8:])
105	adc := binary.BigEndian.Uint16(buf[10:])
106	offset := headerSize
107	var err error
108	for i := uint16(0); i < qdc && offset < buflen; i++ {
109		_, offset, err = UnpackDomainName(buf, offset)
110		if err != nil {
111			return err
112		}
113		// Skip past Type and Class
114		offset += 2 + 2
115	}
116	for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
117		_, offset, err = UnpackDomainName(buf, offset)
118		if err != nil {
119			return err
120		}
121		// Skip past Type, Class and TTL
122		offset += 2 + 2 + 4
123		if offset+1 >= buflen {
124			continue
125		}
126		rdlen := binary.BigEndian.Uint16(buf[offset:])
127		offset += 2
128		offset += int(rdlen)
129	}
130	if offset >= buflen {
131		return &Error{err: "overflowing unpacking signed message"}
132	}
133
134	// offset should be just prior to SIG
135	bodyend := offset
136	// owner name SHOULD be root
137	_, offset, err = UnpackDomainName(buf, offset)
138	if err != nil {
139		return err
140	}
141	// Skip Type, Class, TTL, RDLen
142	offset += 2 + 2 + 4 + 2
143	sigstart := offset
144	// Skip Type Covered, Algorithm, Labels, Original TTL
145	offset += 2 + 1 + 1 + 4
146	if offset+4+4 >= buflen {
147		return &Error{err: "overflow unpacking signed message"}
148	}
149	expire := binary.BigEndian.Uint32(buf[offset:])
150	offset += 4
151	incept := binary.BigEndian.Uint32(buf[offset:])
152	offset += 4
153	now := uint32(time.Now().Unix())
154	if now < incept || now > expire {
155		return ErrTime
156	}
157	// Skip key tag
158	offset += 2
159	var signername string
160	signername, offset, err = UnpackDomainName(buf, offset)
161	if err != nil {
162		return err
163	}
164	// If key has come from the DNS name compression might
165	// have mangled the case of the name
166	if !strings.EqualFold(signername, k.Header().Name) {
167		return &Error{err: "signer name doesn't match key name"}
168	}
169	sigend := offset
170	hasher.Write(buf[sigstart:sigend])
171	hasher.Write(buf[:10])
172	hasher.Write([]byte{
173		byte((adc - 1) << 8),
174		byte(adc - 1),
175	})
176	hasher.Write(buf[12:bodyend])
177
178	hashed := hasher.Sum(nil)
179	sig := buf[sigend:]
180	switch k.Algorithm {
181	case DSA:
182		pk := k.publicKeyDSA()
183		sig = sig[1:]
184		r := new(big.Int).SetBytes(sig[:len(sig)/2])
185		s := new(big.Int).SetBytes(sig[len(sig)/2:])
186		if pk != nil {
187			if dsa.Verify(pk, hashed, r, s) {
188				return nil
189			}
190			return ErrSig
191		}
192	case RSASHA1, RSASHA256, RSASHA512:
193		pk := k.publicKeyRSA()
194		if pk != nil {
195			return rsa.VerifyPKCS1v15(pk, hash, hashed, sig)
196		}
197	case ECDSAP256SHA256, ECDSAP384SHA384:
198		pk := k.publicKeyECDSA()
199		r := new(big.Int).SetBytes(sig[:len(sig)/2])
200		s := new(big.Int).SetBytes(sig[len(sig)/2:])
201		if pk != nil {
202			if ecdsa.Verify(pk, hashed, r, s) {
203				return nil
204			}
205			return ErrSig
206		}
207	}
208	return ErrKeyAlg
209}
210