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