CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-dnspython

DNS toolkit for Python supporting almost all record types with high-level and low-level DNS operations

85

1.37x
Overview
Eval results
Files

dnssec.mddocs/

DNSSEC

DNS Security Extensions (DNSSEC) functionality for validating DNS signatures, handling cryptographic keys, and performing security operations. Provides complete DNSSEC validation and key management capabilities.

Capabilities

Signature Validation

Validate DNSSEC signatures and verify resource record authenticity.

def validate(rrset, rrsigset, keys, origin=None, now=None):
    """
    Validate an RRset against its RRSIG records using provided keys.
    
    Args:
        rrset (dns.rrset.RRset): Resource record set to validate
        rrsigset (dns.rrset.RRset): RRSIG records for validation
        keys (dict): Mapping from key names to DNSKEY records
        origin (dns.name.Name): Origin for relative names
        now (float): Current time (default current time)
        
    Raises:
        dns.dnssec.ValidationFailure: If validation fails
        dns.dnssec.UnsupportedAlgorithm: If algorithm not supported
    """

def validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
    """
    Validate an RRset against a single RRSIG record.
    
    Args:
        rrset (dns.rrset.RRset): Resource record set to validate
        rrsig (dns.rdata.RRSIG): RRSIG record for validation
        keys (dict): Mapping from key names to DNSKEY records
        origin (dns.name.Name): Origin for relative names
        now (float): Current time (default current time)
        
    Raises:
        dns.dnssec.ValidationFailure: If validation fails
        dns.dnssec.UnsupportedAlgorithm: If algorithm not supported
    """

def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
    """
    Internal validation function with detailed error information.
    
    Returns validation status and error details for diagnostic purposes.
    """

Key Operations

Handle DNSKEY records and perform key-related cryptographic operations.

def make_ds(name, key, algorithm, origin=None):
    """
    Create a DS record from a DNSKEY record.
    
    Args:
        name (dns.name.Name): Key owner name
        key (dns.rdata.DNSKEY): DNSKEY record
        algorithm (int): Digest algorithm (1=SHA-1, 2=SHA-256, 4=SHA-384)
        origin (dns.name.Name): Origin for relative names
        
    Returns:
        dns.rdata.DS: Generated DS record
    """

def key_id(key, origin=None):
    """
    Calculate the key ID (key tag) for a DNSKEY record.
    
    Args:
        key (dns.rdata.DNSKEY): DNSKEY record
        origin (dns.name.Name): Origin parameter (historical, not needed)
        
    Returns:
        int: Key tag (0-65535)
    """

def make_cds(name, key, algorithm, origin=None):
    """
    Create a CDS record from a DNSKEY record.
    
    Args:
        name (dns.name.Name): Key owner name
        key (dns.rdata.DNSKEY): DNSKEY record
        algorithm (int): Digest algorithm
        origin (dns.name.Name): Origin for relative names
        
    Returns:
        dns.rdata.CDS: Generated CDS record
    """

def make_cdnskey(key):
    """
    Create a CDNSKEY record from a DNSKEY record.
    
    Args:
        key (dns.rdata.DNSKEY): DNSKEY record
        
    Returns:
        dns.rdata.CDNSKEY: Generated CDNSKEY record
    """

Algorithm Support

DNSSEC algorithm constants and algorithm support functions.

# DNSSEC algorithms
RSAMD5 = 1          # RSA/MD5 (deprecated)
DH = 2              # Diffie-Hellman
DSA = 3             # DSA/SHA-1
ECC = 4             # Elliptic curve cryptography
RSASHA1 = 5         # RSA/SHA-1
DSANSEC3SHA1 = 6    # DSA-NSEC3-SHA1
RSASHA1NSEC3SHA1 = 7 # RSASHA1-NSEC3-SHA1
RSASHA256 = 8       # RSA/SHA-256
RSASHA512 = 10      # RSA/SHA-512
ECCGOST = 12        # GOST R 34.10-2001
ECDSAP256SHA256 = 13 # ECDSA Curve P-256 with SHA-256
ECDSAP384SHA384 = 14 # ECDSA Curve P-384 with SHA-384
ED25519 = 15        # Ed25519
ED448 = 16          # Ed448
PRIVATEDNS = 253    # Private use DNS
PRIVATEOID = 254    # Private use OID

# Digest algorithms
SHA1 = 1            # SHA-1
SHA256 = 2          # SHA-256
GOST = 3            # GOST R 34.11-94
SHA384 = 4          # SHA-384

def algorithm_from_text(text):
    """
    Convert algorithm name to number.
    
    Args:
        text (str): Algorithm name
        
    Returns:
        int: Algorithm number
    """

def algorithm_to_text(algorithm):
    """
    Convert algorithm number to name.
    
    Args:
        algorithm (int): Algorithm number
        
    Returns:
        str: Algorithm name
    """

def _is_rsa(algorithm):
    """Check if algorithm is RSA-based."""

def _is_dsa(algorithm):
    """Check if algorithm is DSA-based."""

def _is_ecdsa(algorithm):
    """Check if algorithm is ECDSA-based."""

def _is_eddsa(algorithm):
    """Check if algorithm is EdDSA-based."""

NSEC and NSEC3 Support

Functions for working with NSEC and NSEC3 records for authenticated denial of existence.

def nsec_matches(name, nsec, origin=None):
    """
    Check if a name matches an NSEC record's coverage.
    
    Args:
        name (dns.name.Name): Name to check
        nsec (dns.rdata.NSEC): NSEC record
        origin (dns.name.Name): Origin for relative names
        
    Returns:
        bool: True if name is covered by NSEC
    """

def nsec3_hash(domain, salt, iterations, algorithm):
    """
    Calculate NSEC3 hash for a domain name.
    
    Args:
        domain (dns.name.Name): Domain name to hash
        salt (bytes): Salt value
        iterations (int): Number of iterations
        algorithm (int): Hash algorithm (1=SHA-1)
        
    Returns:
        bytes: NSEC3 hash value
    """

def nsec3_matches(hash_value, nsec3, origin=None):
    """
    Check if a hash value matches an NSEC3 record's coverage.
    
    Args:
        hash_value (bytes): Hash value to check
        nsec3 (dns.rdata.NSEC3): NSEC3 record
        origin (dns.name.Name): Origin for relative names
        
    Returns:
        bool: True if hash is covered by NSEC3
    """

Signature Creation

Create DNSSEC signatures (requires private keys and cryptographic libraries).

def sign(rrset, key, signer, inception=None, expiration=None, lifetime=None):
    """
    Sign an RRset with a private key.
    
    Args:
        rrset (dns.rrset.RRset): RRset to sign
        key (private key object): Private key for signing
        signer (dns.name.Name): Signer name
        inception (int): Signature inception time
        expiration (int): Signature expiration time
        lifetime (int): Signature lifetime (alternative to expiration)
        
    Returns:
        dns.rdata.RRSIG: Generated RRSIG record
        
    Note:
        Requires cryptographic library (cryptography package)
    """

Usage Examples

Basic DNSSEC Validation

import dns.resolver
import dns.dnssec
import dns.rdatatype

# Query for a DNSSEC-signed domain
resolver = dns.resolver.Resolver()
resolver.use_edns(0, dns.flags.DO, 4096)  # Enable DNSSEC

# Get the DNSKEY records
dnskey_answer = resolver.query('example.com', dns.rdatatype.DNSKEY)
dnskey_rrset = dnskey_answer.rrset

# Get A records and their signatures
a_answer = resolver.query('www.example.com', dns.rdatatype.A)
a_rrset = a_answer.rrset

# Try to get RRSIG records for the A records
try:
    rrsig_answer = resolver.query('www.example.com', dns.rdatatype.RRSIG)
    rrsig_rrset = rrsig_answer.rrset
    
    # Create key dictionary
    keys = {}
    for key in dnskey_rrset:
        key_id = dns.dnssec.key_id(key)
        keys[(dnskey_rrset.name, key_id)] = key
    
    # Validate the signature
    try:
        dns.dnssec.validate(a_rrset, rrsig_rrset, keys)
        print("DNSSEC validation successful!")
    except dns.dnssec.ValidationFailure as e:
        print(f"DNSSEC validation failed: {e}")
        
except dns.resolver.NoAnswer:
    print("No RRSIG records found")

Creating DS Records

import dns.dnssec
import dns.name
import dns.rdata
import dns.rdatatype
import dns.rdataclass

# Create a DNSKEY record (example)
dnskey_text = '256 3 8 AwEAAcX...base64-encoded-key...'
dnskey = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNSKEY, dnskey_text)

# Create DS record with SHA-256
name = dns.name.from_text('example.com.')
ds_sha256 = dns.dnssec.make_ds(name, dnskey, dns.dnssec.SHA256)

print(f"DS record (SHA-256):")
print(f"  Key tag: {ds_sha256.key_tag}")
print(f"  Algorithm: {ds_sha256.algorithm}")
print(f"  Digest type: {ds_sha256.digest_type}")
print(f"  Digest: {ds_sha256.digest.hex().upper()}")

# Create DS record with SHA-1 (less secure, for compatibility)
ds_sha1 = dns.dnssec.make_ds(name, dnskey, dns.dnssec.SHA1)
print(f"\nDS record (SHA-1):")
print(f"  Key tag: {ds_sha1.key_tag}")
print(f"  Digest: {ds_sha1.digest.hex().upper()}")

# Calculate key ID
key_id = dns.dnssec.key_id(dnskey)
print(f"\nDNSKEY key tag: {key_id}")

Algorithm Information

import dns.dnssec

# Work with algorithm names and numbers
algorithms = [
    dns.dnssec.RSASHA256,
    dns.dnssec.RSASHA512,
    dns.dnssec.ECDSAP256SHA256,
    dns.dnssec.ECDSAP384SHA384,
    dns.dnssec.ED25519
]

print("Supported DNSSEC algorithms:")
for alg in algorithms:
    name = dns.dnssec.algorithm_to_text(alg)
    print(f"  {alg}: {name}")

# Convert algorithm names back to numbers
alg_name = 'RSASHA256'
alg_num = dns.dnssec.algorithm_from_text(alg_name)
print(f"\n{alg_name} = {alg_num}")

# Check algorithm types
print(f"\nRSASHA256 is RSA: {dns.dnssec._is_rsa(dns.dnssec.RSASHA256)}")
print(f"ECDSAP256SHA256 is ECDSA: {dns.dnssec._is_ecdsa(dns.dnssec.ECDSAP256SHA256)}")
print(f"ED25519 is EdDSA: {dns.dnssec._is_eddsa(dns.dnssec.ED25519)}")

NSEC3 Operations

import dns.dnssec
import dns.name

# Calculate NSEC3 hash
domain = dns.name.from_text('www.example.com.')
salt = b'\x12\x34\x56\x78'
iterations = 10
algorithm = 1  # SHA-1

hash_value = dns.dnssec.nsec3_hash(domain, salt, iterations, algorithm)
print(f"NSEC3 hash for {domain}: {hash_value.hex().upper()}")

# Base32 encode the hash (typical NSEC3 format)
import base64
b32_hash = base64.b32encode(hash_value).decode().rstrip('=').lower()
print(f"Base32 encoded: {b32_hash}")

Comprehensive Validation Example

import dns.message
import dns.query
import dns.dnssec
import dns.rdatatype
import dns.name

def validate_dnssec_chain(domain, nameserver):
    """Validate DNSSEC chain for a domain."""
    
    # Create query with DNSSEC enabled
    query = dns.message.make_query(domain, dns.rdatatype.A, want_dnssec=True)
    
    # Send query
    response = dns.query.udp(query, nameserver)
    
    if not response.answer:
        print("No answer section in response")
        return False
    
    # Extract RRsets
    answer_rrset = None
    rrsig_rrsets = []
    
    for rrset in response.answer:
        if rrset.rdtype == dns.rdatatype.A:
            answer_rrset = rrset
        elif rrset.rdtype == dns.rdatatype.RRSIG:
            rrsig_rrsets.append(rrset)
    
    if not answer_rrset or not rrsig_rrsets:
        print("Missing required records for validation")
        return False
    
    # Get DNSKEY records for validation
    dnskey_query = dns.message.make_query(domain, dns.rdatatype.DNSKEY, want_dnssec=True)
    dnskey_response = dns.query.udp(dnskey_query, nameserver)
    
    # Build key dictionary
    keys = {}
    for rrset in dnskey_response.answer:
        if rrset.rdtype == dns.rdatatype.DNSKEY:
            for key in rrset:
                key_id = dns.dnssec.key_id(key)
                keys[(rrset.name, key_id)] = key
    
    # Validate signatures
    for rrsig_rrset in rrsig_rrsets:
        try:
            dns.dnssec.validate(answer_rrset, rrsig_rrset, keys)
            print(f"Successfully validated {domain}")
            return True
        except dns.dnssec.ValidationFailure as e:
            print(f"Validation failed: {e}")
            continue
    
    return False

# Example usage
domain = dns.name.from_text('example.com.')
nameserver = '8.8.8.8'
validate_dnssec_chain(domain, nameserver)

Constants

# Key flags
SEP = 0x0001        # Secure Entry Point
REVOKE = 0x0080     # Revoke flag
ZONE = 0x0100       # Zone key flag

# NSEC3 flags  
OPTOUT = 0x01       # Opt-out flag

# Default values
default_algorithm = RSASHA256

Exceptions

class DNSSECError(DNSException):
    """Base class for DNSSEC errors."""

class ValidationFailure(DNSSECError):
    """DNSSEC validation failed."""

class UnsupportedAlgorithm(DNSSECError):
    """Unsupported DNSSEC algorithm."""

class InvalidSignature(ValidationFailure):
    """RRSIG signature is invalid."""

class ExpiredSignature(ValidationFailure):
    """RRSIG signature has expired."""

class NotYetValidSignature(ValidationFailure):
    """RRSIG signature is not yet valid."""

class NoMatchingKey(ValidationFailure):
    """No matching DNSKEY found for RRSIG."""

Install with Tessl CLI

npx tessl i tessl/pypi-dnspython

docs

dns-constants.md

dns-exceptions.md

dns-messages.md

dns-names.md

dns-queries.md

dns-records.md

dns-resolution.md

dns-updates.md

dns-utilities.md

dns-zones.md

dnssec.md

index.md

tsig.md

tile.json