import re
import time
-import dns.dnssec
import dns.exception
import dns.message
import dns.name
import dns.rcode
import dns.rdataclass
import dns.rdatatype
-import dns.rrset
import dns.tsig
import dns.zone
-import dns.zonefile
from isctest.instance import NamedInstance
from isctest.run import EnvCmd
-from isctest.template import TrustAnchor
from isctest.vars.algorithms import ALL_ALGORITHMS_BY_NUM, Algorithm
+from isctest.zone import FileZoneKey
import isctest.log
import isctest.query
@total_ordering
-class Key:
+class Key(FileZoneKey):
"""
- Represent a key from a keyfile.
+ A FileZoneKey specialized with KASP timing and state-file operations.
- This object keeps track of its origin (keydir + name), can be used to
- retrieve metadata from the underlying files and supports convenience
- operations for KASP tests.
+ Inherits the key-material accessors (dnskey, into_ta, ...) from FileZoneKey
+ and adds the metadata reads, signing-state derivation, and timing
+ convenience operations used by the KASP/rollover tests.
"""
def __init__(self, name: str, keydir: str | Path | None = None):
- self.name = name
- if keydir is None:
- self.keydir = Path()
- else:
- self.keydir = Path(keydir)
- self.path = str(self.keydir / name)
- self.privatefile = f"{self.path}.private"
- self.keyfile = f"{self.path}.key"
+ super().__init__(name, keydir)
self.statefile = f"{self.path}.state"
- self.tag = int(self.name[-5:])
self.external = False
def get_timing(
), f"DNSKEY not found in {self.keyfile}"
return dnskey_rr
- def into_ta(self, ta_type: str, dsdigest=dns.dnssec.DSDigest.SHA256) -> TrustAnchor:
- dnskey = self.dnskey
- if ta_type in ["static-ds", "initial-ds"]:
- ds = dns.dnssec.make_ds(dnskey.name, dnskey[0], dsdigest)
- parts = str(ds).split()
- contents = " ".join(parts[:3]) + f' "{parts[3]}"'
- elif ta_type in ["static-key", "initial-key"]:
- parts = str(dnskey).split()
- contents = " ".join(parts[4:7]) + f' "{"".join(parts[7:])}"'
- else:
- raise ValueError(f"invalid trust anchor type: {ta_type}")
- return TrustAnchor(str(dnskey.name), ta_type, contents)
-
def is_ksk(self) -> bool:
+ # KASP role follows the .state KSK metadata, not the DNSKEY SEP flag:
+ # a CSK may be configured without SEP (see the csk-nosep test).
return self.get_metadata("KSK") == "yes"
def is_zsk(self) -> bool:
import dns.rdataclass
import dns.rdatatype
import dns.rrset
+import dns.zonefile
-from .kasp import Key
from .log import debug
from .run import EnvCmd
from .template import NS1, Nameserver, TemplateEngine, TrustAnchor
Abstract base for a DNSSEC key attached to a Zone.
Two concrete implementations exist:
- FileZoneKey — wraps a dnssec-keygen-managed key file pair (kasp.Key).
+ FileZoneKey — reads a dnssec-keygen-managed key file pair.
PythonZoneKey — holds a Python-native (private_key, dnskey_rdata) pair
required for dnspython-based operations and signing.
"""
A ZoneKey backed by dnssec-keygen-managed key files.
- Constructed by FileZoneKey.generate(); callers normally do not
- instantiate this directly. The underlying kasp.Key is accessible via
- .key for working with timing metadata, state files, etc.
+ Reads key material directly from the .key/.private file pair. Normally
+ constructed via FileZoneKey.generate() (or Zone.add_keys());
+ isctest.kasp.Key subclasses it to add KASP timing/state operations.
"""
- def __init__(self, key: Key, zone: Zone) -> None:
- self.key = key
+ def __init__(
+ self,
+ name: str,
+ keydir: str | Path | None = None,
+ zone: Zone | None = None,
+ ) -> None:
+ self.name = name
+ self.keydir = Path() if keydir is None else Path(keydir)
+ self.path = str(self.keydir / name)
+ self.privatefile = f"{self.path}.private"
+ self.keyfile = f"{self.path}.key"
+ self.tag = int(self.name[-5:])
self.zone = zone
@property
def dnskey(self) -> dns.rrset.RRset:
- return self.key.dnskey
+ raise NotImplementedError # TODO will be re-implemented in the followup commit
def write_dsset(
self,
PythonZoneKey KSKs (PythonZoneKey appends to the same file);
Zone.copy_dssets enforces this.
"""
+ assert self.zone is not None, "write_dsset requires a zone-attached key"
src = Path(self.zone.ns.name) / f"dsset-{self.zone.name}."
shutil.copy(src, Path(target_dir))
debug(f"{self.zone.name}: dsset copied to {target_dir}")
"KEYGEN", f"-q -a {alg.number} -b {alg.bits} -K {KEYDIR} -L {DNSKEY_TTL}"
)
key_name = keygen(f"{params} {zone.name}", cwd=zone.ns.name).out.strip()
- return FileZoneKey(Key(key_name, keydir=keydir), zone=zone)
+ return FileZoneKey(key_name, keydir=keydir, zone=zone)
class PythonZoneKey(ZoneKey):