From f1026e21bc1de4a4d83af20d6aca61a10f993d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Mon, 8 Jun 2026 15:33:59 +0000 Subject: [PATCH 1/2] Add malformed ECDSA DNSKEY tests to dnssec_py Port test_malformed_ecdsa and test_multiple_rrsigs from the standalone dnssec_malformed_dnskey directory into the shared dnssec_py fixture harness. The zone is renamed from example. to dnskey-malformed., the resolver fixture changes from a dedicated ns3 to the shared ns9, and trust anchors are wired in via bootstrap() rather than per-directory config files. Assisted-by: Claude:claude-opus-4-8 --- .../dnssec_malformed_dnskey/ns2/named.conf.j2 | 10 - .../ns2/trusted.conf.j2 | 2 - .../tests_malformed_dnskey.py | 176 --------------- bin/tests/system/dnssec_py/common.py | 2 + .../ns2/zones/dnskey-malformed.db.j2.manual} | 17 +- .../dnssec_py/tests_dnskey_malformed.py | 213 ++++++++++++++++++ 6 files changed, 220 insertions(+), 200 deletions(-) rename bin/tests/system/{dnssec_malformed_dnskey/ns2/example.db.in => dnssec_py/ns2/zones/dnskey-malformed.db.j2.manual} (98%) create mode 100644 bin/tests/system/dnssec_py/tests_dnskey_malformed.py diff --git a/bin/tests/system/dnssec_malformed_dnskey/ns2/named.conf.j2 b/bin/tests/system/dnssec_malformed_dnskey/ns2/named.conf.j2 index 75173e22f6..cb89b52a98 100644 --- a/bin/tests/system/dnssec_malformed_dnskey/ns2/named.conf.j2 +++ b/bin/tests/system/dnssec_malformed_dnskey/ns2/named.conf.j2 @@ -9,16 +9,6 @@ options { allow-transfer { any; }; recursion no; dnssec-validation yes; - - /* Keep the order of RRSIGs in the response static. */ - rrset-order { - name "example." order none; - }; -}; - -zone example. { - type primary; - file "example.db.signed.malformed"; }; zone truncated.selfsigned. { diff --git a/bin/tests/system/dnssec_malformed_dnskey/ns2/trusted.conf.j2 b/bin/tests/system/dnssec_malformed_dnskey/ns2/trusted.conf.j2 index 1a0f3c959e..30139fa5aa 100644 --- a/bin/tests/system/dnssec_malformed_dnskey/ns2/trusted.conf.j2 +++ b/bin/tests/system/dnssec_malformed_dnskey/ns2/trusted.conf.j2 @@ -1,6 +1,4 @@ trust-anchors { - example. static-key 257 3 14 "@ksk_public_key@"; - /* * The key tag in the trust anchor must match that of the revoked * truncated self-signed key in the truncated.selfsigned. zone. diff --git a/bin/tests/system/dnssec_malformed_dnskey/tests_malformed_dnskey.py b/bin/tests/system/dnssec_malformed_dnskey/tests_malformed_dnskey.py index cdb8932e7c..cf14498b2a 100644 --- a/bin/tests/system/dnssec_malformed_dnskey/tests_malformed_dnskey.py +++ b/bin/tests/system/dnssec_malformed_dnskey/tests_malformed_dnskey.py @@ -9,186 +9,10 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -from re import compile as Re - import base64 -import os - -from cryptography.hazmat.primitives.asymmetric import ec -from dns.rdtypes.dnskeybase import Flag - -import dns.dnssec -import dns.name -import dns.rdataclass -import dns.rdatatype -import dns.rdtypes.ANY.RRSIG -import dns.zone -import pytest - import isctest -def generate_key(): - algorithm = dns.dnssec.Algorithm.ECDSAP384SHA384 - ksk_private_key = ec.generate_private_key(ec.SECP384R1()) - try: - ksk_dnskey = dns.dnssec.make_dnskey( - public_key=ksk_private_key.public_key(), - algorithm=algorithm, - flags=Flag.ZONE | Flag.SEP, - ) - except ImportError as exc: - # if the cryptography package is too old, the make_dnskey() function - # will raise ImportError at runtime - pytest.skip(f"{exc}") - return ksk_private_key, ksk_dnskey - - -MALFORMED_ZSK_KEY_TAG = 20071 - - -def create_malformed_rr(rr, n=0): - malformed_rr = dns.rdtypes.ANY.RRSIG.RRSIG( - rdclass=rr.rdclass, - rdtype=rr.rdtype, - type_covered=rr.type_covered, - algorithm=rr.algorithm, - labels=rr.labels, - original_ttl=rr.original_ttl - n, # edit TTL so multiple RRSIGs can be added - expiration=rr.expiration, - inception=rr.inception, - key_tag=MALFORMED_ZSK_KEY_TAG, # overwrite with the malformed ZSKs - signer=rr.signer, - signature=rr.signature, - ) - return malformed_rr - - -def bootstrap(): - zone = dns.zone.from_file("ns2/example.db.in", origin="example.") - lifetime = 300 - - # geneate KSK, avoid key tag collision with ZSKs - while True: - ksk_private_key, ksk_dnskey = generate_key() - if dns.dnssec.key_id(ksk_dnskey) != MALFORMED_ZSK_KEY_TAG: - break - keys = [(ksk_private_key, ksk_dnskey)] - - # sign the zone (including the malformed ZSKs) with KSK - with zone.writer() as txn: - dns.dnssec.sign_zone( - zone=zone, - txn=txn, - keys=keys, - lifetime=lifetime, - add_dnskey=True, - deterministic=False, # for OpenSSL<3.2.0 compat - ) - - # force use of the malformed ZSKs for dnssec verification - # malformed-dnskey.example. has only one invalid RRSIG and is only signed - # with malformed ZSKs - malformed_rrset = zone.get_rdataset("malformed-dnskey", "RRSIG", "A") - rr = malformed_rrset.pop() - malformed_rrset.add(create_malformed_rr(rr)) - - # multiple-rrsigs.example. contains a lot of RRSIGS with the same invalid - # signature using malformed RRSIG, and one valid RRSIG - multiple_rrset = zone.get_rdataset("multiple-rrsigs", "RRSIG", "A") - rr = multiple_rrset.pop() - for i in range(99): - multiple_rrset.add(create_malformed_rr(rr, i)) - multiple_rrset.add(rr) - - zone.to_file("ns2/example.db.signed.malformed") - - return { - "ksk_public_key": base64.b64encode(ksk_dnskey.key).decode(), - } - - -def test_malformed_ecdsa(ns3): - log_validation_failed = Re(r"malformed-dnskey\.example/A\): validation failed") - log_openssl_failure = Re("EVP_PKEY_fromdata.*failed") - log_openssl_version = Re("linked to OpenSSL version: OpenSSL ([0-9]+)") - - msg = isctest.query.create("malformed-dnskey.example", "A") - - openssl_vers = ns3.log.grep(log_openssl_version) - if ( - openssl_vers - and int(openssl_vers[0].group(1)) >= 3 - and os.getenv("FEATURE_QUERYTRACE") == "1" - ): - # extra check for OpenSSL 3.0.0+ - with ns3.watch_log_from_here() as watcher: - res = isctest.query.tcp(msg, "10.53.0.3") - - # check the OpenSSL-specific log message appears just once - matches = watcher.wait_for_all( - [ - log_openssl_failure, - log_validation_failed, - ] - ) - assert len([m for m in matches if m.re == log_openssl_failure]) == 1 - else: - res = isctest.query.tcp(msg, "10.53.0.3") - - isctest.check.servfail(res) - - -def test_multiple_rrsigs(ns3): - log_validation_failed = Re(r"multiple-rrsigs\.example/A\): validation failed") - log_openssl_failure = Re("EVP_PKEY_fromdata.*failed") - log_openssl_version = Re("linked to OpenSSL version: OpenSSL ([0-9]+)") - - msg = isctest.query.create("multiple-rrsigs.example", "A") - - # Check the order of returned RRSIGs from auth. Due to rrset-order none; - # this should remain constant for the remainder of the test. - # Ensure the first two RRSIGs are malformed, otherwise skip the test. - res = isctest.query.tcp(msg, "10.53.0.2") - rrsigs = res.get_rrset( - res.answer, - dns.name.from_text("multiple-rrsigs.example."), - dns.rdataclass.IN, - dns.rdatatype.RRSIG, - dns.rdatatype.A, - ) - assert len(rrsigs) > 2 - if ( - rrsigs[0].key_tag != MALFORMED_ZSK_KEY_TAG - or rrsigs[1].key_tag != MALFORMED_ZSK_KEY_TAG - ): - pytest.skip("valid RRSIG listed first in response, re-run test") - - openssl_vers = ns3.log.grep(log_openssl_version) - if ( - openssl_vers - and int(openssl_vers[0].group(1)) >= 3 - and os.getenv("FEATURE_QUERYTRACE") == "1" - ): - # extra check for OpenSSL 3.0.0+ - with ns3.watch_log_from_here() as watcher: - res = isctest.query.tcp(msg, "10.53.0.3") - - # check the OpenSSL-specific log message appears exactly twice: - # one failure is allowed by setting max-validation-failures-per-fetch 1; - matches = watcher.wait_for_all( - [ - log_openssl_failure, - log_validation_failed, - ] - ) - assert len([m for m in matches if m.re == log_openssl_failure]) == 2 - else: - res = isctest.query.tcp(msg, "10.53.0.3") - - isctest.check.servfail(res) - - def test_truncated_dnskey(): msg = isctest.query.create("a.truncated.selfsigned.", "A") res = isctest.query.tcp(msg, "10.53.0.3") diff --git a/bin/tests/system/dnssec_py/common.py b/bin/tests/system/dnssec_py/common.py index 56a30f0c41..8a9fbca4d2 100644 --- a/bin/tests/system/dnssec_py/common.py +++ b/bin/tests/system/dnssec_py/common.py @@ -15,5 +15,7 @@ DNSSEC_PY_MARK = pytest.mark.extra_artifacts( [ "ns*/dsset-*", "ns*/trusted.conf", + "ns*/zones/*.db", + "ns*/zones/*.db.signed", ] ) diff --git a/bin/tests/system/dnssec_malformed_dnskey/ns2/example.db.in b/bin/tests/system/dnssec_py/ns2/zones/dnskey-malformed.db.j2.manual similarity index 98% rename from bin/tests/system/dnssec_malformed_dnskey/ns2/example.db.in rename to bin/tests/system/dnssec_py/ns2/zones/dnskey-malformed.db.j2.manual index 70614af0c3..5925b9701a 100644 --- a/bin/tests/system/dnssec_malformed_dnskey/ns2/example.db.in +++ b/bin/tests/system/dnssec_py/ns2/zones/dnskey-malformed.db.j2.manual @@ -1,18 +1,10 @@ -$TTL 300 -@ IN SOA mname1. . ( - 1 ; serial - 600 ; refresh - 600 ; retry - 1200 ; expire - 600 ; minimum - ) - -@ NS @ -@ A 10.53.0.2 +{% include '_common/zones/soa.partial.db.j2' %} +{% include '_common/zones/ns.partial.db.j2' %} ; All of the following DNSKEYs are malformed and have the same key tag - 20071. ; The keys use invalid parameters for the ECDSA curve. +{% raw %} @ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5Or0NNksES2iAAwmRfEEnH/hzk+8xF @ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfjFg5Y9Ytl2+UR1JO/UNNksES2iAAwmRfEEnH/hzk+8v3 @ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sW2HoOfwFg5Y1ctl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 @@ -112,6 +104,7 @@ $TTL 300 @ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF534ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2h+/wmRfEEnH/hzk+8v3 @ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3QngZjMZtl1Wd/fvtHF/3sU3HoOfwFg5ZFAtl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 @ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +{% endraw %} -malformed-dnskey A 10.53.0.2 +invalid-rrsig A 10.53.0.2 multiple-rrsigs A 10.53.0.2 diff --git a/bin/tests/system/dnssec_py/tests_dnskey_malformed.py b/bin/tests/system/dnssec_py/tests_dnskey_malformed.py new file mode 100644 index 0000000000..2972441b67 --- /dev/null +++ b/bin/tests/system/dnssec_py/tests_dnskey_malformed.py @@ -0,0 +1,213 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +from pathlib import Path +from re import compile as Re + +import base64 +import os + +from cryptography.hazmat.primitives.asymmetric import ec +from dns.rdtypes.dnskeybase import Flag + +import dns.dnssec +import dns.name +import dns.rdataclass +import dns.rdatatype +import dns.rdtypes.ANY.RRSIG +import dns.zone +import pytest + +from dnssec_py.common import DNSSEC_PY_MARK +from isctest.template import NS2, TrustAnchor, zones +from isctest.zone import Zone, configure_root + +import isctest + +pytestmark = DNSSEC_PY_MARK + + +def generate_key(): + algorithm = dns.dnssec.Algorithm.ECDSAP384SHA384 + ksk_private_key = ec.generate_private_key(ec.SECP384R1()) + try: + ksk_dnskey = dns.dnssec.make_dnskey( + public_key=ksk_private_key.public_key(), + algorithm=algorithm, + flags=Flag.ZONE | Flag.SEP, + ) + except ImportError as exc: + # if the cryptography package is too old, the make_dnskey() function + # will raise ImportError at runtime + pytest.skip(f"{exc}") + return ksk_private_key, ksk_dnskey + + +MALFORMED_ZSK_KEY_TAG = 20071 + + +def create_malformed_rr(rr, n=0): + malformed_rr = dns.rdtypes.ANY.RRSIG.RRSIG( + rdclass=rr.rdclass, + rdtype=rr.rdtype, + type_covered=rr.type_covered, + algorithm=rr.algorithm, + labels=rr.labels, + original_ttl=rr.original_ttl - n, # edit TTL so multiple RRSIGs can be added + expiration=rr.expiration, + inception=rr.inception, + key_tag=MALFORMED_ZSK_KEY_TAG, # overwrite with the malformed ZSKs + signer=rr.signer, + signature=rr.signature, + ) + return malformed_rr + + +def bootstrap(): + zone = Zone("dnskey-malformed", NS2, signed=True) + lifetime = 300 + + # generate KSK, avoid key tag collision with ZSKs + while True: + ksk_private_key, ksk_dnskey = generate_key() + if dns.dnssec.key_id(ksk_dnskey) != MALFORMED_ZSK_KEY_TAG: + break + keys = [(ksk_private_key, ksk_dnskey)] + + # render unsigned zone file + zone.render() + + # read the rendered zone + unsigned_path = str(Path(zone.ns.name) / zone.filepath_unsigned) + signed_path = str(Path(zone.ns.name) / zone.filepath_signed) + zoneobj = dns.zone.from_file(unsigned_path, origin="dnskey-malformed.") + + # sign the zone (including the malformed ZSKs) with KSK + with zoneobj.writer() as txn: + dns.dnssec.sign_zone( + zone=zoneobj, + txn=txn, + keys=keys, + lifetime=lifetime, + add_dnskey=True, + deterministic=False, # for OpenSSL<3.2.0 compat + ) + + # force use of the malformed ZSKs for invalid-rrsig.dnskey-malformed; + # the record only has one invalid RRSIG signed with a malformed ZSK + invalid_rrset = zoneobj.get_rdataset("invalid-rrsig", "RRSIG", "A") + rr = invalid_rrset.pop() + invalid_rrset.add(create_malformed_rr(rr)) + + # multiple-rrsigs.dnskey-malformed contains a lot of RRSIGs with the same + # invalid signature using a malformed key, and one valid RRSIG + multiple_rrset = zoneobj.get_rdataset("multiple-rrsigs", "RRSIG", "A") + rr = multiple_rrset.pop() + for i in range(99): + multiple_rrset.add(create_malformed_rr(rr, i)) + multiple_rrset.add(rr) + + zoneobj.to_file(signed_path) + + root = configure_root([zone]) + ksk_key_b64 = base64.b64encode(ksk_dnskey.key).decode() + ksk_ta = TrustAnchor("dnskey-malformed", "static-key", f'257 3 14 "{ksk_key_b64}"') + + return { + "rrset_order_none": ["dnskey-malformed"], + "trust_anchors": [*root.trust_anchors(), ksk_ta], + "zones": zones([root, zone]), + } + + +def test_malformed_ecdsa(ns9): + log_validation_failed = Re( + r"invalid-rrsig\.dnskey-malformed/A\): validation failed" + ) + log_openssl_failure = Re("EVP_PKEY_fromdata.*failed") + log_openssl_version = Re("linked to OpenSSL version: OpenSSL ([0-9]+)") + + msg = isctest.query.create("invalid-rrsig.dnskey-malformed", "A") + + openssl_vers = ns9.log.grep(log_openssl_version) + if ( + openssl_vers + and int(openssl_vers[0].group(1)) >= 3 + and os.getenv("FEATURE_QUERYTRACE") == "1" + ): + # extra check for OpenSSL 3.0.0+ + with ns9.watch_log_from_here() as watcher: + res = isctest.query.tcp(msg, ns9.ip) + + # check the OpenSSL-specific log message appears just once + matches = watcher.wait_for_all( + [ + log_openssl_failure, + log_validation_failed, + ] + ) + assert len([m for m in matches if m.re == log_openssl_failure]) == 1 + else: + res = isctest.query.tcp(msg, ns9.ip) + + isctest.check.servfail(res) + + +def test_multiple_rrsigs(ns2, ns9): + log_validation_failed = Re( + r"multiple-rrsigs\.dnskey-malformed/A\): validation failed" + ) + log_openssl_failure = Re("EVP_PKEY_fromdata.*failed") + log_openssl_version = Re("linked to OpenSSL version: OpenSSL ([0-9]+)") + + msg = isctest.query.create("multiple-rrsigs.dnskey-malformed", "A") + + # Check the order of returned RRSIGs from auth. Due to rrset-order none; + # this should remain constant for the remainder of the test. + # Ensure the first two RRSIGs are malformed, otherwise skip the test. + res = isctest.query.tcp(msg, ns2.ip) + rrsigs = res.get_rrset( + res.answer, + dns.name.from_text("multiple-rrsigs.dnskey-malformed."), + dns.rdataclass.IN, + dns.rdatatype.RRSIG, + dns.rdatatype.A, + ) + assert len(rrsigs) > 2 + if ( + rrsigs[0].key_tag != MALFORMED_ZSK_KEY_TAG + or rrsigs[1].key_tag != MALFORMED_ZSK_KEY_TAG + ): + pytest.skip("valid RRSIG listed first in response, re-run test") + + openssl_vers = ns9.log.grep(log_openssl_version) + if ( + openssl_vers + and int(openssl_vers[0].group(1)) >= 3 + and os.getenv("FEATURE_QUERYTRACE") == "1" + ): + # extra check for OpenSSL 3.0.0+ + with ns9.watch_log_from_here() as watcher: + res = isctest.query.tcp(msg, ns9.ip) + + # check the OpenSSL-specific log message appears exactly twice: + # one failure is allowed by setting max-validation-failures-per-fetch 1; + matches = watcher.wait_for_all( + [ + log_openssl_failure, + log_validation_failed, + ] + ) + assert len([m for m in matches if m.re == log_openssl_failure]) == 2 + else: + res = isctest.query.tcp(msg, ns9.ip) + + isctest.check.servfail(res) From 1d6866b0ee3167df6114c4943060ca810a8df58f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Mon, 8 Jun 2026 15:34:35 +0000 Subject: [PATCH 2/2] Add revoked truncated self-signed DNSKEY test to dnssec_py Port test_truncated_dnskey from dnssec_malformed_dnskey into the shared dnssec_py fixture harness, completing the migration and deleting the remaining dnssec_malformed_dnskey files. Assisted-by: Claude:claude-opus-4-8 --- .../dnssec_malformed_dnskey/ns2/named.conf.j2 | 19 -------- .../ns2/trusted.conf.j2 | 12 ----- .../dnssec_malformed_dnskey/ns3/named.conf.j2 | 26 ----------- .../ns3/trusted.conf.j2 | 1 - .../tests_malformed_dnskey.py | 19 -------- .../zones/truncated.selfsigned.db.signed.j2} | 2 + .../tests_dnskey_truncated_selfsigned.py | 45 +++++++++++++++++++ 7 files changed, 47 insertions(+), 77 deletions(-) delete mode 100644 bin/tests/system/dnssec_malformed_dnskey/ns2/named.conf.j2 delete mode 100644 bin/tests/system/dnssec_malformed_dnskey/ns2/trusted.conf.j2 delete mode 100644 bin/tests/system/dnssec_malformed_dnskey/ns3/named.conf.j2 delete mode 120000 bin/tests/system/dnssec_malformed_dnskey/ns3/trusted.conf.j2 delete mode 100644 bin/tests/system/dnssec_malformed_dnskey/tests_malformed_dnskey.py rename bin/tests/system/{dnssec_malformed_dnskey/ns2/truncated.selfsigned.db.signed => dnssec_py/ns2/zones/truncated.selfsigned.db.signed.j2} (98%) create mode 100644 bin/tests/system/dnssec_py/tests_dnskey_truncated_selfsigned.py diff --git a/bin/tests/system/dnssec_malformed_dnskey/ns2/named.conf.j2 b/bin/tests/system/dnssec_malformed_dnskey/ns2/named.conf.j2 deleted file mode 100644 index cb89b52a98..0000000000 --- a/bin/tests/system/dnssec_malformed_dnskey/ns2/named.conf.j2 +++ /dev/null @@ -1,19 +0,0 @@ -options { - query-source address 10.53.0.2; - notify-source 10.53.0.2; - transfer-source 10.53.0.2; - port @PORT@; - pid-file "named.pid"; - listen-on { 10.53.0.2; }; - listen-on-v6 { none; }; - allow-transfer { any; }; - recursion no; - dnssec-validation yes; -}; - -zone truncated.selfsigned. { - type primary; - file "truncated.selfsigned.db.signed"; -}; - -include "trusted.conf"; diff --git a/bin/tests/system/dnssec_malformed_dnskey/ns2/trusted.conf.j2 b/bin/tests/system/dnssec_malformed_dnskey/ns2/trusted.conf.j2 deleted file mode 100644 index 30139fa5aa..0000000000 --- a/bin/tests/system/dnssec_malformed_dnskey/ns2/trusted.conf.j2 +++ /dev/null @@ -1,12 +0,0 @@ -trust-anchors { - /* - * The key tag in the trust anchor must match that of the revoked - * truncated self-signed key in the truncated.selfsigned. zone. - * - * The DNSKEY contents are intentionally different here, because the - * key doesn't have the revoked bit here and that flag is part of the - * key tag. The following decodes to key tag 33167, which is the same - * as the revoked truncated key in the zone file. - */ - truncated.selfsigned. static-key 257 3 14 "fYA="; -}; diff --git a/bin/tests/system/dnssec_malformed_dnskey/ns3/named.conf.j2 b/bin/tests/system/dnssec_malformed_dnskey/ns3/named.conf.j2 deleted file mode 100644 index 939c7b8c35..0000000000 --- a/bin/tests/system/dnssec_malformed_dnskey/ns3/named.conf.j2 +++ /dev/null @@ -1,26 +0,0 @@ -options { - query-source address 10.53.0.3; - notify-source 10.53.0.3; - transfer-source 10.53.0.3; - port @PORT@; - pid-file "named.pid"; - listen-on { 10.53.0.3; }; - listen-on-v6 { none; }; - allow-transfer { any; }; - dnssec-validation yes; - - /* This is the default, but the test relies on it. */ - max-validation-failures-per-fetch 1; -}; - -zone "example." { - type static-stub; - server-addresses { 10.53.0.2; }; -}; - -zone "truncated.selfsigned." { - type static-stub; - server-addresses { 10.53.0.2; }; -}; - -include "trusted.conf"; diff --git a/bin/tests/system/dnssec_malformed_dnskey/ns3/trusted.conf.j2 b/bin/tests/system/dnssec_malformed_dnskey/ns3/trusted.conf.j2 deleted file mode 120000 index e14af83a92..0000000000 --- a/bin/tests/system/dnssec_malformed_dnskey/ns3/trusted.conf.j2 +++ /dev/null @@ -1 +0,0 @@ -../ns2/trusted.conf.j2 \ No newline at end of file diff --git a/bin/tests/system/dnssec_malformed_dnskey/tests_malformed_dnskey.py b/bin/tests/system/dnssec_malformed_dnskey/tests_malformed_dnskey.py deleted file mode 100644 index cf14498b2a..0000000000 --- a/bin/tests/system/dnssec_malformed_dnskey/tests_malformed_dnskey.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (C) Internet Systems Consortium, Inc. ("ISC") -# -# SPDX-License-Identifier: MPL-2.0 -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, you can obtain one at https://mozilla.org/MPL/2.0/. -# -# See the COPYRIGHT file distributed with this work for additional -# information regarding copyright ownership. - -import base64 -import isctest - - -def test_truncated_dnskey(): - msg = isctest.query.create("a.truncated.selfsigned.", "A") - res = isctest.query.tcp(msg, "10.53.0.3") - isctest.check.servfail(res) diff --git a/bin/tests/system/dnssec_malformed_dnskey/ns2/truncated.selfsigned.db.signed b/bin/tests/system/dnssec_py/ns2/zones/truncated.selfsigned.db.signed.j2 similarity index 98% rename from bin/tests/system/dnssec_malformed_dnskey/ns2/truncated.selfsigned.db.signed rename to bin/tests/system/dnssec_py/ns2/zones/truncated.selfsigned.db.signed.j2 index 1a74fd566f..533dbca091 100644 --- a/bin/tests/system/dnssec_malformed_dnskey/ns2/truncated.selfsigned.db.signed +++ b/bin/tests/system/dnssec_py/ns2/zones/truncated.selfsigned.db.signed.j2 @@ -1,3 +1,4 @@ +{% raw %} $TTL 300 @ IN SOA mname1. . ( @@ -27,3 +28,4 @@ a A 10.53.0.2 a RRSIG A 14 3 86400 20950926153053 20251013153053 33167 @ xxxxv31CNatB9xzj3AfTMlwiO0OqxbpJ cWrHN8zjj1ScXpqrHITfG/CZpoECDLWF wkXshDB/QMxHrnXkPKEcR2c9o5tcQT5R nHvtr7HT4Ob5PcY5DnItf3OWhE+bocmW a NSEC @ A RRSIG NSEC a RRSIG NSEC 14 3 0 20950926153053 20251013153053 33167 @ xxxxwMWbUxb3ScBKEVheQ2wFqujc6cyt 28GVCU0wPrBpK72HSsgdYme7IG8ZXGfa IWSU1Kf/om5+El7Tf2vDs7aI1yI7e7YG D5IxMejQg5v3/wtP7AJZXP5K9ICjq/ph +{% endraw %} diff --git a/bin/tests/system/dnssec_py/tests_dnskey_truncated_selfsigned.py b/bin/tests/system/dnssec_py/tests_dnskey_truncated_selfsigned.py new file mode 100644 index 0000000000..fa6ceea85f --- /dev/null +++ b/bin/tests/system/dnssec_py/tests_dnskey_truncated_selfsigned.py @@ -0,0 +1,45 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +from re import compile as Re + +from dnssec_py.common import DNSSEC_PY_MARK +from isctest.template import NS2, TrustAnchor, zones +from isctest.zone import Zone, configure_root + +import isctest + +pytestmark = DNSSEC_PY_MARK + + +def bootstrap(): + zone = Zone("truncated.selfsigned", NS2, signed=True) + + root = configure_root([zone], signed=False) # just delegation, TA is added directly + + # The trust anchor key tag must match the revoked truncated self-signed key + # in the zone (key tag 33167). The flags differ here (257 vs 385) because + # the revoked bit is not part of the trust anchor, but it is part of the key + # tag calculation. + zone_ta = TrustAnchor("truncated.selfsigned", "static-key", '257 3 14 "fYA="') + + return { + "trust_anchors": [zone_ta], + "zones": zones([root, zone]), + } + + +def test_truncated_dnskey(ns9): + msg = isctest.query.create("a.truncated.selfsigned.", "A") + with ns9.watch_log_from_here() as watcher: + res = isctest.query.tcp(msg, ns9.ip) + watcher.wait_for_line(Re("a.truncated.selfsigned/A.*broken trust chain")) + isctest.check.servfail(res)