0
# DNSSEC
1
2
DNS Security Extensions (DNSSEC) functionality for validating DNS signatures, handling cryptographic keys, and performing security operations. Provides complete DNSSEC validation and key management capabilities.
3
4
## Capabilities
5
6
### Signature Validation
7
8
Validate DNSSEC signatures and verify resource record authenticity.
9
10
```python { .api }
11
def validate(rrset, rrsigset, keys, origin=None, now=None):
12
"""
13
Validate an RRset against its RRSIG records using provided keys.
14
15
Args:
16
rrset (dns.rrset.RRset): Resource record set to validate
17
rrsigset (dns.rrset.RRset): RRSIG records for validation
18
keys (dict): Mapping from key names to DNSKEY records
19
origin (dns.name.Name): Origin for relative names
20
now (float): Current time (default current time)
21
22
Raises:
23
dns.dnssec.ValidationFailure: If validation fails
24
dns.dnssec.UnsupportedAlgorithm: If algorithm not supported
25
"""
26
27
def validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
28
"""
29
Validate an RRset against a single RRSIG record.
30
31
Args:
32
rrset (dns.rrset.RRset): Resource record set to validate
33
rrsig (dns.rdata.RRSIG): RRSIG record for validation
34
keys (dict): Mapping from key names to DNSKEY records
35
origin (dns.name.Name): Origin for relative names
36
now (float): Current time (default current time)
37
38
Raises:
39
dns.dnssec.ValidationFailure: If validation fails
40
dns.dnssec.UnsupportedAlgorithm: If algorithm not supported
41
"""
42
43
def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
44
"""
45
Internal validation function with detailed error information.
46
47
Returns validation status and error details for diagnostic purposes.
48
"""
49
```
50
51
### Key Operations
52
53
Handle DNSKEY records and perform key-related cryptographic operations.
54
55
```python { .api }
56
def make_ds(name, key, algorithm, origin=None):
57
"""
58
Create a DS record from a DNSKEY record.
59
60
Args:
61
name (dns.name.Name): Key owner name
62
key (dns.rdata.DNSKEY): DNSKEY record
63
algorithm (int): Digest algorithm (1=SHA-1, 2=SHA-256, 4=SHA-384)
64
origin (dns.name.Name): Origin for relative names
65
66
Returns:
67
dns.rdata.DS: Generated DS record
68
"""
69
70
def key_id(key, origin=None):
71
"""
72
Calculate the key ID (key tag) for a DNSKEY record.
73
74
Args:
75
key (dns.rdata.DNSKEY): DNSKEY record
76
origin (dns.name.Name): Origin parameter (historical, not needed)
77
78
Returns:
79
int: Key tag (0-65535)
80
"""
81
82
def make_cds(name, key, algorithm, origin=None):
83
"""
84
Create a CDS record from a DNSKEY record.
85
86
Args:
87
name (dns.name.Name): Key owner name
88
key (dns.rdata.DNSKEY): DNSKEY record
89
algorithm (int): Digest algorithm
90
origin (dns.name.Name): Origin for relative names
91
92
Returns:
93
dns.rdata.CDS: Generated CDS record
94
"""
95
96
def make_cdnskey(key):
97
"""
98
Create a CDNSKEY record from a DNSKEY record.
99
100
Args:
101
key (dns.rdata.DNSKEY): DNSKEY record
102
103
Returns:
104
dns.rdata.CDNSKEY: Generated CDNSKEY record
105
"""
106
```
107
108
### Algorithm Support
109
110
DNSSEC algorithm constants and algorithm support functions.
111
112
```python { .api }
113
# DNSSEC algorithms
114
RSAMD5 = 1 # RSA/MD5 (deprecated)
115
DH = 2 # Diffie-Hellman
116
DSA = 3 # DSA/SHA-1
117
ECC = 4 # Elliptic curve cryptography
118
RSASHA1 = 5 # RSA/SHA-1
119
DSANSEC3SHA1 = 6 # DSA-NSEC3-SHA1
120
RSASHA1NSEC3SHA1 = 7 # RSASHA1-NSEC3-SHA1
121
RSASHA256 = 8 # RSA/SHA-256
122
RSASHA512 = 10 # RSA/SHA-512
123
ECCGOST = 12 # GOST R 34.10-2001
124
ECDSAP256SHA256 = 13 # ECDSA Curve P-256 with SHA-256
125
ECDSAP384SHA384 = 14 # ECDSA Curve P-384 with SHA-384
126
ED25519 = 15 # Ed25519
127
ED448 = 16 # Ed448
128
PRIVATEDNS = 253 # Private use DNS
129
PRIVATEOID = 254 # Private use OID
130
131
# Digest algorithms
132
SHA1 = 1 # SHA-1
133
SHA256 = 2 # SHA-256
134
GOST = 3 # GOST R 34.11-94
135
SHA384 = 4 # SHA-384
136
137
def algorithm_from_text(text):
138
"""
139
Convert algorithm name to number.
140
141
Args:
142
text (str): Algorithm name
143
144
Returns:
145
int: Algorithm number
146
"""
147
148
def algorithm_to_text(algorithm):
149
"""
150
Convert algorithm number to name.
151
152
Args:
153
algorithm (int): Algorithm number
154
155
Returns:
156
str: Algorithm name
157
"""
158
159
def _is_rsa(algorithm):
160
"""Check if algorithm is RSA-based."""
161
162
def _is_dsa(algorithm):
163
"""Check if algorithm is DSA-based."""
164
165
def _is_ecdsa(algorithm):
166
"""Check if algorithm is ECDSA-based."""
167
168
def _is_eddsa(algorithm):
169
"""Check if algorithm is EdDSA-based."""
170
```
171
172
### NSEC and NSEC3 Support
173
174
Functions for working with NSEC and NSEC3 records for authenticated denial of existence.
175
176
```python { .api }
177
def nsec_matches(name, nsec, origin=None):
178
"""
179
Check if a name matches an NSEC record's coverage.
180
181
Args:
182
name (dns.name.Name): Name to check
183
nsec (dns.rdata.NSEC): NSEC record
184
origin (dns.name.Name): Origin for relative names
185
186
Returns:
187
bool: True if name is covered by NSEC
188
"""
189
190
def nsec3_hash(domain, salt, iterations, algorithm):
191
"""
192
Calculate NSEC3 hash for a domain name.
193
194
Args:
195
domain (dns.name.Name): Domain name to hash
196
salt (bytes): Salt value
197
iterations (int): Number of iterations
198
algorithm (int): Hash algorithm (1=SHA-1)
199
200
Returns:
201
bytes: NSEC3 hash value
202
"""
203
204
def nsec3_matches(hash_value, nsec3, origin=None):
205
"""
206
Check if a hash value matches an NSEC3 record's coverage.
207
208
Args:
209
hash_value (bytes): Hash value to check
210
nsec3 (dns.rdata.NSEC3): NSEC3 record
211
origin (dns.name.Name): Origin for relative names
212
213
Returns:
214
bool: True if hash is covered by NSEC3
215
"""
216
```
217
218
### Signature Creation
219
220
Create DNSSEC signatures (requires private keys and cryptographic libraries).
221
222
```python { .api }
223
def sign(rrset, key, signer, inception=None, expiration=None, lifetime=None):
224
"""
225
Sign an RRset with a private key.
226
227
Args:
228
rrset (dns.rrset.RRset): RRset to sign
229
key (private key object): Private key for signing
230
signer (dns.name.Name): Signer name
231
inception (int): Signature inception time
232
expiration (int): Signature expiration time
233
lifetime (int): Signature lifetime (alternative to expiration)
234
235
Returns:
236
dns.rdata.RRSIG: Generated RRSIG record
237
238
Note:
239
Requires cryptographic library (cryptography package)
240
"""
241
```
242
243
## Usage Examples
244
245
### Basic DNSSEC Validation
246
247
```python
248
import dns.resolver
249
import dns.dnssec
250
import dns.rdatatype
251
252
# Query for a DNSSEC-signed domain
253
resolver = dns.resolver.Resolver()
254
resolver.use_edns(0, dns.flags.DO, 4096) # Enable DNSSEC
255
256
# Get the DNSKEY records
257
dnskey_answer = resolver.query('example.com', dns.rdatatype.DNSKEY)
258
dnskey_rrset = dnskey_answer.rrset
259
260
# Get A records and their signatures
261
a_answer = resolver.query('www.example.com', dns.rdatatype.A)
262
a_rrset = a_answer.rrset
263
264
# Try to get RRSIG records for the A records
265
try:
266
rrsig_answer = resolver.query('www.example.com', dns.rdatatype.RRSIG)
267
rrsig_rrset = rrsig_answer.rrset
268
269
# Create key dictionary
270
keys = {}
271
for key in dnskey_rrset:
272
key_id = dns.dnssec.key_id(key)
273
keys[(dnskey_rrset.name, key_id)] = key
274
275
# Validate the signature
276
try:
277
dns.dnssec.validate(a_rrset, rrsig_rrset, keys)
278
print("DNSSEC validation successful!")
279
except dns.dnssec.ValidationFailure as e:
280
print(f"DNSSEC validation failed: {e}")
281
282
except dns.resolver.NoAnswer:
283
print("No RRSIG records found")
284
```
285
286
### Creating DS Records
287
288
```python
289
import dns.dnssec
290
import dns.name
291
import dns.rdata
292
import dns.rdatatype
293
import dns.rdataclass
294
295
# Create a DNSKEY record (example)
296
dnskey_text = '256 3 8 AwEAAcX...base64-encoded-key...'
297
dnskey = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNSKEY, dnskey_text)
298
299
# Create DS record with SHA-256
300
name = dns.name.from_text('example.com.')
301
ds_sha256 = dns.dnssec.make_ds(name, dnskey, dns.dnssec.SHA256)
302
303
print(f"DS record (SHA-256):")
304
print(f" Key tag: {ds_sha256.key_tag}")
305
print(f" Algorithm: {ds_sha256.algorithm}")
306
print(f" Digest type: {ds_sha256.digest_type}")
307
print(f" Digest: {ds_sha256.digest.hex().upper()}")
308
309
# Create DS record with SHA-1 (less secure, for compatibility)
310
ds_sha1 = dns.dnssec.make_ds(name, dnskey, dns.dnssec.SHA1)
311
print(f"\nDS record (SHA-1):")
312
print(f" Key tag: {ds_sha1.key_tag}")
313
print(f" Digest: {ds_sha1.digest.hex().upper()}")
314
315
# Calculate key ID
316
key_id = dns.dnssec.key_id(dnskey)
317
print(f"\nDNSKEY key tag: {key_id}")
318
```
319
320
### Algorithm Information
321
322
```python
323
import dns.dnssec
324
325
# Work with algorithm names and numbers
326
algorithms = [
327
dns.dnssec.RSASHA256,
328
dns.dnssec.RSASHA512,
329
dns.dnssec.ECDSAP256SHA256,
330
dns.dnssec.ECDSAP384SHA384,
331
dns.dnssec.ED25519
332
]
333
334
print("Supported DNSSEC algorithms:")
335
for alg in algorithms:
336
name = dns.dnssec.algorithm_to_text(alg)
337
print(f" {alg}: {name}")
338
339
# Convert algorithm names back to numbers
340
alg_name = 'RSASHA256'
341
alg_num = dns.dnssec.algorithm_from_text(alg_name)
342
print(f"\n{alg_name} = {alg_num}")
343
344
# Check algorithm types
345
print(f"\nRSASHA256 is RSA: {dns.dnssec._is_rsa(dns.dnssec.RSASHA256)}")
346
print(f"ECDSAP256SHA256 is ECDSA: {dns.dnssec._is_ecdsa(dns.dnssec.ECDSAP256SHA256)}")
347
print(f"ED25519 is EdDSA: {dns.dnssec._is_eddsa(dns.dnssec.ED25519)}")
348
```
349
350
### NSEC3 Operations
351
352
```python
353
import dns.dnssec
354
import dns.name
355
356
# Calculate NSEC3 hash
357
domain = dns.name.from_text('www.example.com.')
358
salt = b'\x12\x34\x56\x78'
359
iterations = 10
360
algorithm = 1 # SHA-1
361
362
hash_value = dns.dnssec.nsec3_hash(domain, salt, iterations, algorithm)
363
print(f"NSEC3 hash for {domain}: {hash_value.hex().upper()}")
364
365
# Base32 encode the hash (typical NSEC3 format)
366
import base64
367
b32_hash = base64.b32encode(hash_value).decode().rstrip('=').lower()
368
print(f"Base32 encoded: {b32_hash}")
369
```
370
371
### Comprehensive Validation Example
372
373
```python
374
import dns.message
375
import dns.query
376
import dns.dnssec
377
import dns.rdatatype
378
import dns.name
379
380
def validate_dnssec_chain(domain, nameserver):
381
"""Validate DNSSEC chain for a domain."""
382
383
# Create query with DNSSEC enabled
384
query = dns.message.make_query(domain, dns.rdatatype.A, want_dnssec=True)
385
386
# Send query
387
response = dns.query.udp(query, nameserver)
388
389
if not response.answer:
390
print("No answer section in response")
391
return False
392
393
# Extract RRsets
394
answer_rrset = None
395
rrsig_rrsets = []
396
397
for rrset in response.answer:
398
if rrset.rdtype == dns.rdatatype.A:
399
answer_rrset = rrset
400
elif rrset.rdtype == dns.rdatatype.RRSIG:
401
rrsig_rrsets.append(rrset)
402
403
if not answer_rrset or not rrsig_rrsets:
404
print("Missing required records for validation")
405
return False
406
407
# Get DNSKEY records for validation
408
dnskey_query = dns.message.make_query(domain, dns.rdatatype.DNSKEY, want_dnssec=True)
409
dnskey_response = dns.query.udp(dnskey_query, nameserver)
410
411
# Build key dictionary
412
keys = {}
413
for rrset in dnskey_response.answer:
414
if rrset.rdtype == dns.rdatatype.DNSKEY:
415
for key in rrset:
416
key_id = dns.dnssec.key_id(key)
417
keys[(rrset.name, key_id)] = key
418
419
# Validate signatures
420
for rrsig_rrset in rrsig_rrsets:
421
try:
422
dns.dnssec.validate(answer_rrset, rrsig_rrset, keys)
423
print(f"Successfully validated {domain}")
424
return True
425
except dns.dnssec.ValidationFailure as e:
426
print(f"Validation failed: {e}")
427
continue
428
429
return False
430
431
# Example usage
432
domain = dns.name.from_text('example.com.')
433
nameserver = '8.8.8.8'
434
validate_dnssec_chain(domain, nameserver)
435
```
436
437
## Constants
438
439
```python { .api }
440
# Key flags
441
SEP = 0x0001 # Secure Entry Point
442
REVOKE = 0x0080 # Revoke flag
443
ZONE = 0x0100 # Zone key flag
444
445
# NSEC3 flags
446
OPTOUT = 0x01 # Opt-out flag
447
448
# Default values
449
default_algorithm = RSASHA256
450
```
451
452
## Exceptions
453
454
```python { .api }
455
class DNSSECError(DNSException):
456
"""Base class for DNSSEC errors."""
457
458
class ValidationFailure(DNSSECError):
459
"""DNSSEC validation failed."""
460
461
class UnsupportedAlgorithm(DNSSECError):
462
"""Unsupported DNSSEC algorithm."""
463
464
class InvalidSignature(ValidationFailure):
465
"""RRSIG signature is invalid."""
466
467
class ExpiredSignature(ValidationFailure):
468
"""RRSIG signature has expired."""
469
470
class NotYetValidSignature(ValidationFailure):
471
"""RRSIG signature is not yet valid."""
472
473
class NoMatchingKey(ValidationFailure):
474
"""No matching DNSKEY found for RRSIG."""
475
```