Exemplo n.º 1
0
def nsec3hash(name, algnum, salt, iterations, binary_out=False):
    """
    Compute NSEC3 hash for given domain name and parameters. name is
    of type dns.name.Name, salt is a binary bytestring, algnum and
    iterations are integers.
    """

    if iterations < 0:
        raise ResError("NSEC3 hash iterations must be >= 0")
    if iterations > Prefs.N3_HASHLIMIT:
        raise ResError("NSEC3 hash iterations too high: {} {}".format(
            name, iterations))

    hashfunc = nsec3_hashalg(algnum)
    digest = name.to_digestable()
    while iterations >= 0:
        d = hashes.Hash(hashfunc(), backend=default_backend())
        d.update(digest + salt)
        digest = d.finalize()
        iterations -= 1
    if binary_out:
        return digest

    output = base64.b32encode(digest)
    output = output.translate(b32_to_ext_hex).decode()
    return output
Exemplo n.º 2
0
def nsec_nxdomain_proof(qname, signer, nsec_list):
    """
    Check NSEC NXDOMAIN proof for given qname, zone, and NSEC list.
    Raise exception if not proved.
    """

    qname_cover = False
    wildcard_cover = False

    for rrset in nsec_list:
        if nsec_covers_name(rrset, qname):
            qname_cover = True

    if not qname_cover:
        raise ResError("No NSEC covering qname {} found.".format(qname))

    wildcard = nsec_wildcard_at_closest_encloser(qname, signer, nsec_list)

    for rrset in nsec_list:
        if nsec_covers_name(rrset, wildcard):
            wildcard_cover = True
            break

    if not wildcard_cover:
        raise ResError("No NSEC covering wildcard {} found.".format(wildcard))
Exemplo n.º 3
0
 def check_time(self, skew=CLOCK_SKEW):
     """
     Check that current time is within signature validity period,
     modulo an allowable clock skew interval.
     """
     current_time = int(time.time() + 0.5)
     if current_time < (self.rdata.inception - skew):
         raise ResError("Signature inception in future: {}".format(
             time.asctime(time.gmtime(self.rdata.inception))))
     if current_time > (self.rdata.expiration + skew):
         raise ResError("Signature has expired: {}".format(
             time.asctime(time.gmtime(self.rdata.expiration))))
Exemplo n.º 4
0
def check_self_signature(rrset, rrsigs):
    """
    Check self signature of DNSKEY rrset. Raises exception on failure.
    Returns list of DNSKEY keys in the rrset, and the list of the subset
    of those keys that verifiably sign the DNSKEY rrset.
    """

    Verified = []
    Failed = []
    keys, errors = load_keys(rrset)

    for sig in get_sig_info(rrset, rrsigs):
        v, f = verify_sig_with_keys(sig, keys)
        Verified += v
        Failed += f

    if errors:
        print("ERROR: DNSKEY errors: {}".format(errors))
    if Failed:
        print("ERROR: DNSKEY self signature failed: {}".format(Failed))
    if not Verified:
        raise ResError(
            "DNSKEY {} self signatures failed to validate: {}".format(
                rrset.name, Failed))

    return keys, Verified
Exemplo n.º 5
0
 def verify(self, dnskey):
     """
     Verify signature with specified key. Raises a crypto key
     specific exception on failure.
     """
     pubkey = dnskey.key
     hashalg = HASHFUNC[dnskey.algorithm]
     if dnskey.algorithm in [5, 7, 8, 10]:
         _ = pubkey.verify(self.rdata.signature, self.indata,
                           padding.PKCS1v15(), hashalg())
     elif dnskey.algorithm in [13, 14]:
         if dnskey.algorithm == 13:
             sig_r = self.rdata.signature[0:32]
             sig_s = self.rdata.signature[32:]
         elif dnskey.algorithm == 14:
             sig_r = self.rdata.signature[0:48]
             sig_s = self.rdata.signature[48:]
         sig_r = int.from_bytes(sig_r, byteorder='big')
         sig_s = int.from_bytes(sig_s, byteorder='big')
         encoded_sig = utils.encode_dss_signature(sig_r, sig_s)
         _ = pubkey.verify(encoded_sig, self.indata, ec.ECDSA(hashalg()))
     elif dnskey.algorithm in [15, 16]:
         _ = pubkey.verify(self.rdata.signature, self.indata)
     else:
         raise ResError("Unknown key type: {}".format(type(pubkey)))
Exemplo n.º 6
0
 def add_to_full_answer(self, srrset):
     """add rrset to full answer set"""
     names_and_types = [(x.rrset.name, x.rrset.rdtype)
                        for x in self.full_answer_rrset]
     if (srrset.rrname, srrset.rrtype) in names_and_types:
         raise ResError("RRset answer loop detected: {}".format(
             srrset.rrset))
     self.full_answer_rrset.append(srrset)
Exemplo n.º 7
0
def keydata_to_eddsa(algnum, keydata):
    """Convert raw keydata into an EdDSA key object"""
    if algnum == 15:
        return ed25519.Ed25519PublicKey.from_public_bytes(keydata)
    elif algnum == 16:
        return ed448.Ed448PublicKey.from_public_bytes(keydata)
    else:
        raise ResError("Unknown EdDSA algorithm number {}".format(algnum))
Exemplo n.º 8
0
def nsec3_hashalg(algnum):
    """
    Return NSEC3 hash function; only SHA1 supported at this time in
    the DNSSEC specifications.
    """
    if algnum == 1:
        return hashes.SHA1
    else:
        raise ResError("unsupported NSEC3 hash algorithm {}".format(algnum))
Exemplo n.º 9
0
def keydata_to_ecc(algnum, keydata):
    """Convert raw keydata into an ECC key object"""
    if algnum == 13:
        point_length = 32
        curve = ec.SECP256R1()
    elif algnum == 14:
        point_length = 48
        curve = ec.SECP384R1()
    else:
        raise ResError("Invalid algorithm number {} for ECDSA".format(algnum))
    x = int.from_bytes(keydata[0:point_length], byteorder='big')
    y = int.from_bytes(keydata[point_length:], byteorder='big')
    return ec.EllipticCurvePublicNumbers(curve=curve, x=x,
                                         y=y).public_key(default_backend())
Exemplo n.º 10
0
 def __init__(self, rrname, rr):
     self.name = rrname
     self.flags = rr.flags
     self.protocol = rr.protocol
     self.algorithm = rr.algorithm
     self.rawkey = rr.key
     self.keytag = dns.dnssec.key_id(rr)
     self.sep_flag = (self.flags & 0x01) == 0x01
     self.zone_flag = (self.flags & 0x0100) == 0x0100
     self.revoke_flag = (self.flags & 0x0080) == 0x0080
     if not self.rawkey:
         raise ResError(
             "DNSKEY keytag={} alg={} has null length data".format(
                 self.keytag, self.algorithm))
     if self.algorithm in [5, 7, 8, 10]:
         self.key = keydata_to_rsa(rr.key)
     elif self.algorithm in [13, 14]:
         self.key = keydata_to_ecc(self.algorithm, rr.key)
     elif self.algorithm in [15, 16]:
         self.key = keydata_to_eddsa(self.algorithm, rr.key)
     else:
         raise ResError("DNSKEY algorithm {} not supported".format(
             self.algorithm))
Exemplo n.º 11
0
def nsec_closest_encloser(qname, zonename, nsec_list):
    """
    Given qname, zone name, and the set of related NSEC records, return
    the closest encloser name.
    """

    if not qname.is_subdomain(zonename):
        raise ResError("qname is not subdomain of ancestor")
    resultlist = []
    q = qname
    while q != zonename:
        q = q.parent()
        resultlist.append(q)
    resultlist.reverse()

    candidate = None
    for candidate in resultlist:
        for nsec in nsec_list:
            if nsec_covers_name(nsec, candidate):
                return candidate.parent()
    return candidate
Exemplo n.º 12
0
def validate_all(rrset, rrsigs):
    """
    Validate rrsigs for rrset with the already authenticated global cache
    of keys in key_cache. Returns a tuple:
    Verified - list of keys that verified the signature.
    Failed - list of (key, error) tuples for failed keys.
    """

    Verified = []
    Failed = []

    for sig in get_sig_info(rrset, rrsigs):
        keylist = key_cache.get_keys(sig.rdata.signer)
        if keylist is None:
            raise ResError("No DNSSEC keys found for {}".format(
                sig.rdata.signer))
        v, f = verify_sig_with_keys(sig, keylist)
        Verified += v
        Failed += f

    return Verified, Failed
Exemplo n.º 13
0
def nsec3_wildcard_nodata_proof(qname, qtype, signer, nsec3_list, quiet=False):
    """
    NSEC3 wildcard NODATA proof for given qname, zone, and NSEC3 list.

    From RFC 5155, Section 8.7:
    8.7.  Validating Wildcard No Data Responses

    The validator MUST verify a closest encloser proof for QNAME and MUST
    find an NSEC3 RR present in the response that matches the wildcard
    name generated by prepending the asterisk label to the closest
    encloser.  Furthermore, the bits corresponding to both QTYPE and
    CNAME MUST NOT be set in the wildcard matching NSEC3 RR.
    """

    closest_encloser_match = False
    wildcard_match = False

    closest_encloser, _ = nsec3_closest_encloser_and_next(
        qname, signer, nsec3_list)
    wildcard = dns.name.Name(('*', ) + closest_encloser.labels)

    for nsec3 in nsec3_list:
        hashed_ce = nsec3hashname_from_record(closest_encloser, nsec3, signer)
        hashed_wild = nsec3hashname_from_record(wildcard, nsec3, signer)
        if nsec3.name == hashed_ce:
            closest_encloser_match = True
            if Prefs.VERBOSE and not quiet:
                print("# INFO: closest encloser: {} {}".format(
                    closest_encloser, hashed_ce.labels[0].decode()))
        if nsec3.name == hashed_wild:
            if (not type_in_bitmap(qtype, nsec3[0])
                    and not type_in_bitmap(dns.rdatatype.CNAME, nsec3[0])):
                wildcard_match = True
                if Prefs.VERBOSE and not quiet:
                    print("# INFO: wildcard: {} {}".format(
                        wildcard, hashed_wild.labels[0].decode()))

    if not (closest_encloser_match and wildcard_match):
        raise ResError("{} NSEC3 Wildcard NODATA proof failed.".format(qname))
    return wildcard
Exemplo n.º 14
0
def nsec3_nxdomain_proof(qname, signer, nsec3_list, optout=False, quiet=False):
    """
    Check NSEC3 NXDOMAIN proof for given qname, zone, and NSEC list.
    Raise exception if not proved.
    """

    closest_encloser_match = False
    next_closer_cover = False
    wildcard_cover = optout

    closest_encloser, next_closer = nsec3_closest_encloser_and_next(
        qname, signer, nsec3_list)
    wildcard = dns.name.Name(('*', ) + closest_encloser.labels)
    for nsec3 in nsec3_list:
        hashed_ce = nsec3hashname_from_record(closest_encloser, nsec3, signer)
        hashed_nc = nsec3hashname_from_record(next_closer, nsec3, signer)
        hashed_wild = nsec3hashname_from_record(wildcard, nsec3, signer)
        if nsec3.name == hashed_ce:
            closest_encloser_match = True
            if Prefs.VERBOSE and not quiet:
                print("# INFO: closest{} encloser: {} {}".format(
                    " provable" if optout else "", closest_encloser,
                    hashed_ce.labels[0].decode()))
        if nsec3_covers_name(nsec3, hashed_nc, signer):
            if optout:
                if not nsec3[0].flags & 0x1:
                    continue
            next_closer_cover = True
            if Prefs.VERBOSE and not quiet:
                print("# INFO: next closer: {} {}".format(
                    next_closer, hashed_nc.labels[0].decode()))
        if not optout and nsec3_covers_name(nsec3, hashed_wild, signer):
            wildcard_cover = True
            if Prefs.VERBOSE and not quiet:
                print("# INFO: wildcard: {} {}".format(
                    wildcard, hashed_wild.labels[0].decode()))

    if not (closest_encloser_match and next_closer_cover and wildcard_cover):
        raise ResError("{} NSEC3 NXDOMAIN proof failed.".format(qname))
Exemplo n.º 15
0
def nsec3_closest_encloser_and_next(qname, zonename, nsec3_list):
    """
    Given qname and an zone name and the set of relavent NSEC3 records,
    return the closest encloser name and the next closer name.
    """

    if not qname.is_subdomain(zonename):
        raise ResError("qname is not subdomain of zone")
    resultlist = []
    q = qname
    while q != zonename:
        q = q.parent()
        resultlist.append(q)
    resultlist.reverse()

    candidate = None
    for candidate in resultlist:
        for nsec3 in nsec3_list:
            hashed_name = nsec3hashname_from_record(candidate, nsec3, zonename)
            if nsec3_covers_name(nsec3, hashed_name, zonename):
                return candidate.parent(), candidate
    return candidate, qname