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