1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS TSIG support."""
17
18 import hmac
19 import struct
20
21 import dns.exception
22 import dns.hash
23 import dns.rdataclass
24 import dns.name
25 from ._compat import long, string_types, text_type
26
27 -class BadTime(dns.exception.DNSException):
28
29 """The current time is not within the TSIG's validity time."""
30
31
33
34 """The TSIG signature fails to verify."""
35
36
38
39 """Base class for all TSIG errors generated by the remote peer"""
40
41
43
44 """The peer didn't know the key we used"""
45
46
48
49 """The peer didn't like the signature we sent"""
50
51
53
54 """The peer didn't like the time we sent"""
55
56
58
59 """The peer didn't like amount of truncation in the TSIG we sent"""
60
61
62
63 HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT")
64 HMAC_SHA1 = dns.name.from_text("hmac-sha1")
65 HMAC_SHA224 = dns.name.from_text("hmac-sha224")
66 HMAC_SHA256 = dns.name.from_text("hmac-sha256")
67 HMAC_SHA384 = dns.name.from_text("hmac-sha384")
68 HMAC_SHA512 = dns.name.from_text("hmac-sha512")
69
70 _hashes = {
71 HMAC_SHA224: 'SHA224',
72 HMAC_SHA256: 'SHA256',
73 HMAC_SHA384: 'SHA384',
74 HMAC_SHA512: 'SHA512',
75 HMAC_SHA1: 'SHA1',
76 HMAC_MD5: 'MD5',
77 }
78
79 default_algorithm = HMAC_MD5
80
81 BADSIG = 16
82 BADKEY = 17
83 BADTIME = 18
84 BADTRUNC = 22
85
86
87 -def sign(wire, keyname, secret, time, fudge, original_id, error,
88 other_data, request_mac, ctx=None, multi=False, first=True,
89 algorithm=default_algorithm):
90 """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata
91 for the input parameters, the HMAC MAC calculated by applying the
92 TSIG signature algorithm, and the TSIG digest context.
93 @rtype: (string, string, hmac.HMAC object)
94 @raises ValueError: I{other_data} is too long
95 @raises NotImplementedError: I{algorithm} is not supported
96 """
97
98 if isinstance(other_data, text_type):
99 other_data = other_data.encode()
100 (algorithm_name, digestmod) = get_algorithm(algorithm)
101 if first:
102 ctx = hmac.new(secret, digestmod=digestmod)
103 ml = len(request_mac)
104 if ml > 0:
105 ctx.update(struct.pack('!H', ml))
106 ctx.update(request_mac)
107 id = struct.pack('!H', original_id)
108 ctx.update(id)
109 ctx.update(wire[2:])
110 if first:
111 ctx.update(keyname.to_digestable())
112 ctx.update(struct.pack('!H', dns.rdataclass.ANY))
113 ctx.update(struct.pack('!I', 0))
114 long_time = time + long(0)
115 upper_time = (long_time >> 32) & long(0xffff)
116 lower_time = long_time & long(0xffffffff)
117 time_mac = struct.pack('!HIH', upper_time, lower_time, fudge)
118 pre_mac = algorithm_name + time_mac
119 ol = len(other_data)
120 if ol > 65535:
121 raise ValueError('TSIG Other Data is > 65535 bytes')
122 post_mac = struct.pack('!HH', error, ol) + other_data
123 if first:
124 ctx.update(pre_mac)
125 ctx.update(post_mac)
126 else:
127 ctx.update(time_mac)
128 mac = ctx.digest()
129 mpack = struct.pack('!H', len(mac))
130 tsig_rdata = pre_mac + mpack + mac + id + post_mac
131 if multi:
132 ctx = hmac.new(secret, digestmod=digestmod)
133 ml = len(mac)
134 ctx.update(struct.pack('!H', ml))
135 ctx.update(mac)
136 else:
137 ctx = None
138 return (tsig_rdata, mac, ctx)
139
140
141 -def hmac_md5(wire, keyname, secret, time, fudge, original_id, error,
142 other_data, request_mac, ctx=None, multi=False, first=True,
143 algorithm=default_algorithm):
144 return sign(wire, keyname, secret, time, fudge, original_id, error,
145 other_data, request_mac, ctx, multi, first, algorithm)
146
147
148 -def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata,
149 tsig_rdlen, ctx=None, multi=False, first=True):
150 """Validate the specified TSIG rdata against the other input parameters.
151
152 @raises FormError: The TSIG is badly formed.
153 @raises BadTime: There is too much time skew between the client and the
154 server.
155 @raises BadSignature: The TSIG signature did not validate
156 @rtype: hmac.HMAC object"""
157
158 (adcount,) = struct.unpack("!H", wire[10:12])
159 if adcount == 0:
160 raise dns.exception.FormError
161 adcount -= 1
162 new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start]
163 current = tsig_rdata
164 (aname, used) = dns.name.from_wire(wire, current)
165 current = current + used
166 (upper_time, lower_time, fudge, mac_size) = \
167 struct.unpack("!HIHH", wire[current:current + 10])
168 time = ((upper_time + long(0)) << 32) + (lower_time + long(0))
169 current += 10
170 mac = wire[current:current + mac_size]
171 current += mac_size
172 (original_id, error, other_size) = \
173 struct.unpack("!HHH", wire[current:current + 6])
174 current += 6
175 other_data = wire[current:current + other_size]
176 current += other_size
177 if current != tsig_rdata + tsig_rdlen:
178 raise dns.exception.FormError
179 if error != 0:
180 if error == BADSIG:
181 raise PeerBadSignature
182 elif error == BADKEY:
183 raise PeerBadKey
184 elif error == BADTIME:
185 raise PeerBadTime
186 elif error == BADTRUNC:
187 raise PeerBadTruncation
188 else:
189 raise PeerError('unknown TSIG error code %d' % error)
190 time_low = time - fudge
191 time_high = time + fudge
192 if now < time_low or now > time_high:
193 raise BadTime
194 (junk, our_mac, ctx) = sign(new_wire, keyname, secret, time, fudge,
195 original_id, error, other_data,
196 request_mac, ctx, multi, first, aname)
197 if our_mac != mac:
198 raise BadSignature
199 return ctx
200
201
218
219
221 """Return the tsig algorithm for the specified tsig_rdata
222 @raises FormError: The TSIG is badly formed.
223 """
224 current = tsig_rdata
225 (aname, used) = dns.name.from_wire(wire, current)
226 current = current + used
227 (upper_time, lower_time, fudge, mac_size) = \
228 struct.unpack("!HIHH", wire[current:current + 10])
229 current += 10
230 mac = wire[current:current + mac_size]
231 current += mac_size
232 if current > tsig_rdata + tsig_rdlen:
233 raise dns.exception.FormError
234 return (aname, mac)
235