17 // HMAC hashing codes. These are transmitted as domain names.
19 HmacMD5 = "hmac-md5.sig-alg.reg.int."
20 HmacSHA1 = "hmac-sha1."
21 HmacSHA256 = "hmac-sha256."
22 HmacSHA512 = "hmac-sha512."
25 // TSIG is the RR the holds the transaction signature of a message.
26 // See RFC 2845 and RFC 4635.
29 Algorithm string `dns:"domain-name"`
30 TimeSigned uint64 `dns:"uint48"`
33 MAC string `dns:"size-hex:MACSize"`
37 OtherData string `dns:"size-hex:OtherLen"`
40 // TSIG has no official presentation format, but this will suffice.
42 func (rr *TSIG) String() string {
43 s := "\n;; TSIG PSEUDOSECTION:\n"
44 s += rr.Hdr.String() +
46 " " + tsigTimeToString(rr.TimeSigned) +
47 " " + strconv.Itoa(int(rr.Fudge)) +
48 " " + strconv.Itoa(int(rr.MACSize)) +
49 " " + strings.ToUpper(rr.MAC) +
50 " " + strconv.Itoa(int(rr.OrigId)) +
51 " " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR
52 " " + strconv.Itoa(int(rr.OtherLen)) +
57 func (rr *TSIG) parse(c *zlexer, origin, file string) *ParseError {
58 panic("dns: internal error: parse should never be called on TSIG")
61 // The following values must be put in wireformat, so that the MAC can be calculated.
62 // RFC 2845, section 3.4.2. TSIG Variables.
63 type tsigWireFmt struct {
65 Name string `dns:"domain-name"`
69 Algorithm string `dns:"domain-name"`
70 TimeSigned uint64 `dns:"uint48"`
72 // MACSize, MAC and OrigId excluded
75 OtherData string `dns:"size-hex:OtherLen"`
78 // If we have the MAC use this type to convert it to wiredata. Section 3.4.3. Request MAC
79 type macWireFmt struct {
81 MAC string `dns:"size-hex:MACSize"`
84 // 3.3. Time values used in TSIG calculations
85 type timerWireFmt struct {
86 TimeSigned uint64 `dns:"uint48"`
90 // TsigGenerate fills out the TSIG record attached to the message.
91 // The message should contain
92 // a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
93 // time fudge (defaults to 300 seconds) and the current time
94 // The TSIG MAC is saved in that Tsig RR.
95 // When TsigGenerate is called for the first time requestMAC is set to the empty string and
96 // timersOnly is false.
97 // If something goes wrong an error is returned, otherwise it is nil.
98 func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
99 if m.IsTsig() == nil {
100 panic("dns: TSIG not last RR in additional")
102 // If we barf here, the caller is to blame
103 rawsecret, err := fromBase64([]byte(secret))
108 rr := m.Extra[len(m.Extra)-1].(*TSIG)
109 m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
110 mbuf, err := m.Pack()
114 buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
118 switch strings.ToLower(rr.Algorithm) {
120 h = hmac.New(md5.New, rawsecret)
122 h = hmac.New(sha1.New, rawsecret)
124 h = hmac.New(sha256.New, rawsecret)
126 h = hmac.New(sha512.New, rawsecret)
128 return nil, "", ErrKeyAlg
131 t.MAC = hex.EncodeToString(h.Sum(nil))
132 t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
134 t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
136 t.TimeSigned = rr.TimeSigned
137 t.Algorithm = rr.Algorithm
140 tbuf := make([]byte, Len(t))
141 off, err := PackRR(t, tbuf, 0, nil, false)
145 mbuf = append(mbuf, tbuf[:off]...)
146 // Update the ArCount directly in the buffer.
147 binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1))
149 return mbuf, t.MAC, nil
152 // TsigVerify verifies the TSIG on a message.
153 // If the signature does not validate err contains the
154 // error, otherwise it is nil.
155 func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
156 rawsecret, err := fromBase64([]byte(secret))
160 // Strip the TSIG from the incoming msg
161 stripped, tsig, err := stripTsig(msg)
166 msgMAC, err := hex.DecodeString(tsig.MAC)
171 buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
173 // Fudge factor works both ways. A message can arrive before it was signed because
175 now := uint64(time.Now().Unix())
176 ti := now - tsig.TimeSigned
177 if now < tsig.TimeSigned {
178 ti = tsig.TimeSigned - now
180 if uint64(tsig.Fudge) < ti {
185 switch strings.ToLower(tsig.Algorithm) {
187 h = hmac.New(md5.New, rawsecret)
189 h = hmac.New(sha1.New, rawsecret)
191 h = hmac.New(sha256.New, rawsecret)
193 h = hmac.New(sha512.New, rawsecret)
198 if !hmac.Equal(h.Sum(nil), msgMAC) {
204 // Create a wiredata buffer for the MAC calculation.
205 func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte {
207 if rr.TimeSigned == 0 {
208 rr.TimeSigned = uint64(time.Now().Unix())
211 rr.Fudge = 300 // Standard (RFC) default.
214 // Replace message ID in header with original ID from TSIG
215 binary.BigEndian.PutUint16(msgbuf[0:2], rr.OrigId)
217 if requestMAC != "" {
219 m.MACSize = uint16(len(requestMAC) / 2)
221 buf = make([]byte, len(requestMAC)) // long enough
222 n, _ := packMacWire(m, buf)
226 tsigvar := make([]byte, DefaultMsgSize)
228 tsig := new(timerWireFmt)
229 tsig.TimeSigned = rr.TimeSigned
230 tsig.Fudge = rr.Fudge
231 n, _ := packTimerWire(tsig, tsigvar)
232 tsigvar = tsigvar[:n]
234 tsig := new(tsigWireFmt)
235 tsig.Name = strings.ToLower(rr.Hdr.Name)
236 tsig.Class = ClassANY
237 tsig.Ttl = rr.Hdr.Ttl
238 tsig.Algorithm = strings.ToLower(rr.Algorithm)
239 tsig.TimeSigned = rr.TimeSigned
240 tsig.Fudge = rr.Fudge
241 tsig.Error = rr.Error
242 tsig.OtherLen = rr.OtherLen
243 tsig.OtherData = rr.OtherData
244 n, _ := packTsigWire(tsig, tsigvar)
245 tsigvar = tsigvar[:n]
248 if requestMAC != "" {
249 x := append(buf, msgbuf...)
250 buf = append(x, tsigvar...)
252 buf = append(msgbuf, tsigvar...)
257 // Strip the TSIG from the raw message.
258 func stripTsig(msg []byte) ([]byte, *TSIG, error) {
259 // Copied from msg.go's Unpack() Header, but modified.
266 if dh, off, err = unpackMsgHdr(msg, off); err != nil {
270 return nil, nil, ErrNoSig
273 // Rcode, see msg.go Unpack()
274 if int(dh.Bits&0xF) == RcodeNotAuth {
275 return nil, nil, ErrAuth
278 for i := 0; i < int(dh.Qdcount); i++ {
279 _, off, err = unpackQuestion(msg, off)
285 _, off, err = unpackRRslice(int(dh.Ancount), msg, off)
289 _, off, err = unpackRRslice(int(dh.Nscount), msg, off)
296 for i := 0; i < int(dh.Arcount); i++ {
298 extra, off, err = UnpackRR(msg, off)
302 if extra.Header().Rrtype == TypeTSIG {
305 arcount := binary.BigEndian.Uint16(msg[10:])
306 binary.BigEndian.PutUint16(msg[10:], arcount-1)
311 return nil, nil, ErrNoSig
313 return msg[:tsigoff], rr, nil
316 // Translate the TSIG time signed into a date. There is no
317 // need for RFC1982 calculations as this date is 48 bits.
318 func tsigTimeToString(t uint64) string {
319 ti := time.Unix(int64(t), 0).UTC()
320 return ti.Format("20060102150405")
323 func packTsigWire(tw *tsigWireFmt, msg []byte) (int, error) {
324 // copied from zmsg.go TSIG packing
326 off, err := PackDomainName(tw.Name, msg, 0, nil, false)
330 off, err = packUint16(tw.Class, msg, off)
334 off, err = packUint32(tw.Ttl, msg, off)
339 off, err = PackDomainName(tw.Algorithm, msg, off, nil, false)
343 off, err = packUint48(tw.TimeSigned, msg, off)
347 off, err = packUint16(tw.Fudge, msg, off)
352 off, err = packUint16(tw.Error, msg, off)
356 off, err = packUint16(tw.OtherLen, msg, off)
360 off, err = packStringHex(tw.OtherData, msg, off)
367 func packMacWire(mw *macWireFmt, msg []byte) (int, error) {
368 off, err := packUint16(mw.MACSize, msg, 0)
372 off, err = packStringHex(mw.MAC, msg, off)
379 func packTimerWire(tw *timerWireFmt, msg []byte) (int, error) {
380 off, err := packUint48(tw.TimeSigned, msg, 0)
384 off, err = packUint16(tw.Fudge, msg, off)