]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
rollover-ksk-doubleksk: From setup.sh to pytest bootstrap
authorMatthijs Mekking <matthijs@isc.org>
Fri, 28 Nov 2025 10:38:06 +0000 (11:38 +0100)
committerMatthijs Mekking <matthijs@isc.org>
Fri, 19 Dec 2025 10:47:50 +0000 (11:47 +0100)
Symlink ns1 and ns2 to rollover/ns1 and rollover/ns2.
Symlink ns3/template.db.j2.manual to rollover/ns3/template.db.j2.manual.

Since the bootstrapping is done before the templates are rendered
automatically, replace @DEFAULT_ALGORITHM@ in ns3/kasp.conf.j2 to
ecdsa256 and rename to ns3/kasp.conf.

bin/tests/system/rollover-ksk-3crowd/ns3/kasp.conf [moved from bin/tests/system/rollover-ksk-3crowd/ns3/kasp.conf.j2 with 100% similarity]
bin/tests/system/rollover-ksk-3crowd/ns3/template.db.in [deleted symlink]
bin/tests/system/rollover-ksk-doubleksk/ns1 [new symlink]
bin/tests/system/rollover-ksk-doubleksk/ns2 [new symlink]
bin/tests/system/rollover-ksk-doubleksk/ns3/kasp.conf [moved from bin/tests/system/rollover-ksk-doubleksk/ns3/kasp.conf.j2 with 80% similarity]
bin/tests/system/rollover-ksk-doubleksk/ns3/template.db.in [deleted symlink]
bin/tests/system/rollover-ksk-doubleksk/ns3/template.db.j2.manual [new symlink]
bin/tests/system/rollover-ksk-doubleksk/ns3/trusted.conf.j2 [new symlink]
bin/tests/system/rollover-ksk-doubleksk/setup.sh [deleted file]
bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py
bin/tests/system/rollover/setup.py

diff --git a/bin/tests/system/rollover-ksk-3crowd/ns3/template.db.in b/bin/tests/system/rollover-ksk-3crowd/ns3/template.db.in
deleted file mode 120000 (symlink)
index ce6d526..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../../rollover/ns3/template.db.in
\ No newline at end of file
diff --git a/bin/tests/system/rollover-ksk-doubleksk/ns1 b/bin/tests/system/rollover-ksk-doubleksk/ns1
new file mode 120000 (symlink)
index 0000000..76608be
--- /dev/null
@@ -0,0 +1 @@
+../rollover/ns1
\ No newline at end of file
diff --git a/bin/tests/system/rollover-ksk-doubleksk/ns2 b/bin/tests/system/rollover-ksk-doubleksk/ns2
new file mode 120000 (symlink)
index 0000000..41a09bb
--- /dev/null
@@ -0,0 +1 @@
+../rollover/ns2
\ No newline at end of file
similarity index 80%
rename from bin/tests/system/rollover-ksk-doubleksk/ns3/kasp.conf.j2
rename to bin/tests/system/rollover-ksk-doubleksk/ns3/kasp.conf
index c33a9f7c40240d2f982602258fa43784bf3efeb1..5d0643b421bb09a65558cc6a5918158702eb2ae6 100644 (file)
@@ -23,8 +23,8 @@ dnssec-policy "ksk-doubleksk-autosign" {
 
        cdnskey no;
        keys {
-               ksk key-directory lifetime P60D algorithm @DEFAULT_ALGORITHM@;
-               zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
+               ksk key-directory lifetime P60D algorithm ecdsa256;
+               zsk key-directory lifetime unlimited algorithm ecdsa256;
        };
 
        zone-propagation-delay PT1H;
@@ -48,8 +48,8 @@ dnssec-policy "ksk-doubleksk-manual" {
 
        cdnskey no;
        keys {
-               ksk key-directory lifetime P60D algorithm @DEFAULT_ALGORITHM@;
-               zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
+               ksk key-directory lifetime P60D algorithm ecdsa256;
+               zsk key-directory lifetime unlimited algorithm ecdsa256;
        };
 
        zone-propagation-delay PT1H;
diff --git a/bin/tests/system/rollover-ksk-doubleksk/ns3/template.db.in b/bin/tests/system/rollover-ksk-doubleksk/ns3/template.db.in
deleted file mode 120000 (symlink)
index ce6d526..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../../rollover/ns3/template.db.in
\ No newline at end of file
diff --git a/bin/tests/system/rollover-ksk-doubleksk/ns3/template.db.j2.manual b/bin/tests/system/rollover-ksk-doubleksk/ns3/template.db.j2.manual
new file mode 120000 (symlink)
index 0000000..38619a0
--- /dev/null
@@ -0,0 +1 @@
+../../rollover/ns3/template.db.j2.manual
\ No newline at end of file
diff --git a/bin/tests/system/rollover-ksk-doubleksk/ns3/trusted.conf.j2 b/bin/tests/system/rollover-ksk-doubleksk/ns3/trusted.conf.j2
new file mode 120000 (symlink)
index 0000000..cb0be77
--- /dev/null
@@ -0,0 +1 @@
+../../_common/trusted.conf.j2
\ No newline at end of file
diff --git a/bin/tests/system/rollover-ksk-doubleksk/setup.sh b/bin/tests/system/rollover-ksk-doubleksk/setup.sh
deleted file mode 100644 (file)
index cfd654b..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-#!/bin/sh -e
-
-# 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.
-
-# shellcheck source=conf.sh
-. ../conf.sh
-
-cd "ns3"
-
-setup() {
-  zone="$1"
-  echo_i "setting up zone: $zone"
-  zonefile="${zone}.db"
-  infile="${zone}.db.infile"
-  echo "$zone" >>zones
-}
-
-# Set in the key state files the Predecessor/Successor fields.
-# Key $1 is the predecessor of key $2.
-key_successor() {
-  id1=$(keyfile_to_key_id "$1")
-  id2=$(keyfile_to_key_id "$2")
-  echo "Predecessor: ${id1}" >>"${2}.state"
-  echo "Successor: ${id2}" >>"${1}.state"
-}
-
-# Make lines shorter by storing key states in environment variables.
-H="HIDDEN"
-R="RUMOURED"
-O="OMNIPRESENT"
-U="UNRETENTIVE"
-
-#
-# The zones at ksk-doubleksk.$tld represent the various steps of a KSK
-# Double-KSK rollover.
-#
-
-for tld in autosign manual; do
-  # Step 1:
-  # Introduce the first key. This will immediately be active.
-  setup step1.ksk-doubleksk.$tld
-  TactN="now-7d"
-  keytimes="-P ${TactN} -A ${TactN}"
-  KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $keytimes $zone 2>keygen.out.$zone.1)
-  ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $keytimes $zone 2>keygen.out.$zone.2)
-  $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1
-  cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 2:
-  # It is time to submit the introduce the new KSK.
-  setup step2.ksk-doubleksk.$tld
-  # Lksk:           60d
-  # Dreg:           n/a
-  # DprpC:          1h
-  # TTLds:          1d
-  # TTLkey:         2h
-  # publish-safety: 1d
-  # retire-safety:  2d
-  #
-  # According to RFC 7583:
-  # Tpub(N+1) <= Tact(N) + Lksk - Dreg - IpubC
-  # IpubC = DprpC + TTLkey (+publish-safety)
-  #
-  # IpubC   = 27h
-  # Tact(N) = now - Lksk + Dreg + IpubC = now - 60d + 27h
-  #         = now - 1440h + 27h = now - 1413h
-  TactN="now-1413h"
-  keytimes="-P ${TactN} -A ${TactN}"
-  KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $keytimes $zone 2>keygen.out.$zone.1)
-  ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $keytimes $zone 2>keygen.out.$zone.2)
-  $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1
-  cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 3:
-  # It is time to submit the DS.
-  setup step3.ksk-doubleksk.$tld
-  # According to RFC 7583:
-  # Iret = DprpP + TTLds (+retire-safety)
-  #
-  # Iret       = 50h
-  # Tpub(N)    = now - Lksk = now - 60d = now - 60d
-  # Tact(N)    = now - 1413h
-  # Tret(N)    = now
-  # Trem(N)    = now + Iret = now + 50h
-  # Tpub(N+1)  = now - IpubC = now - 27h
-  # Tact(N+1)  = now
-  # Tret(N+1)  = now + Lksk = now + 60d
-  # Trem(N+1)  = now + Lksk + Iret = now + 60d + 50h
-  #            = now + 1440h + 50h = 1490h
-  TpubN="now-60d"
-  TactN="now-1413h"
-  TretN="now"
-  TremN="now+50h"
-  TpubN1="now-27h"
-  TactN1="now"
-  TretN1="now+60d"
-  TremN1="now+1490h"
-  ksktimes="-P ${TpubN}  -A ${TpubN}  -P sync ${TactN}  -I ${TretN}  -D ${TremN} -D sync ${TactN1}"
-  newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}"
-  zsktimes="-P ${TpubN}  -A ${TpubN}"
-  KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-  KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2)
-  ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3)
-  $SETTIME -s -g $H -k $O $TpubN -r $O $TpubN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.2 2>&1
-  $SETTIME -s -g $O -k $O $TpubN -z $O $TpubN "$ZSK" >settime.out.$zone.3 2>&1
-  # Set key rollover relationship.
-  key_successor $KSK1 $KSK2
-  # Sign zone.
-  cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 4:
-  # The DS should be swapped now.
-  setup step4.ksk-doubleksk.$tld
-  # Tpub(N)    = now - Lksk - Iret = now - 60d - 50h
-  #            = now - 1440h - 50h = now - 1490h
-  # Tact(N)    = now - 1490h + 27h = now - 1463h
-  # Tret(N)    = now - Iret = now - 50h
-  # Trem(N)    = now
-  # Tpub(N+1)  = now - Iret - IpubC = now - 50h - 27h
-  #            = now - 77h
-  # Tact(N+1)  = Tret(N)
-  # Tret(N+1)  = now + Lksk - Iret = now + 60d - 50h = now + 1390h
-  # Trem(N+1)  = now + Lksk = now + 60d
-  TpubN="now-1490h"
-  TactN="now-1463h"
-  TretN="now-50h"
-  TremN="now"
-  TpubN1="now-77h"
-  TactN1="${TretN}"
-  TretN1="now+1390h"
-  TremN1="now+60d"
-  ksktimes="-P ${TpubN}  -A ${TpubN}  -P sync ${TactN}  -I ${TretN}   -D ${TremN} -D sync ${TactN1}"
-  newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1}  -D ${TremN1}"
-  zsktimes="-P ${TpubN}  -A ${TpubN}"
-  KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-  KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2)
-  ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3)
-  $SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $U $TretN -D ds $TretN "$KSK1" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $R $TactN1 -P ds $TactN1 "$KSK2" >settime.out.$zone.2 2>&1
-  $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1
-  # Set key rollover relationship.
-  key_successor $KSK1 $KSK2
-  # Sign zone.
-  cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 5:
-  # The predecessor DNSKEY is removed long enough that is has become HIDDEN.
-  setup step5.ksk-doubleksk.$tld
-  # Subtract DNSKEY TTL + zone-propagation-delay from all the times (3h).
-  # Tpub(N)    = now - 1490h - 3h = now - 1493h
-  # Tact(N)    = now - 1463h - 3h = now - 1466h
-  # Tret(N)    = now - 50h - 3h = now - 53h
-  # Trem(N)    = now - 3h
-  # Tpub(N+1)  = now - 77h - 3h = now - 80h
-  # Tact(N+1)  = Tret(N)
-  # Tret(N+1)  = now + 1390h - 3h = now + 1387h
-  # Trem(N+1)  = now + 60d - 3h = now + 1441h
-  TpubN="now-1493h"
-  TactN="now-1466h"
-  TretN="now-53h"
-  TremN="now-3h"
-  TpubN1="now-80h"
-  TactN1="${TretN}"
-  TretN1="now+1387h"
-  TremN1="now+1441h"
-  ksktimes="-P ${TpubN}  -A ${TpubN}  -P sync ${TactN}  -I ${TretN}  -D ${TremN} -D sync ${TactN1}"
-  newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}"
-  zsktimes="-P ${TpubN}  -A ${TpubN}"
-  KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-  KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2)
-  ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3)
-  $SETTIME -s -g $H -k $U $TretN -r $U $TretN -d $H $TretN "$KSK1" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 "$KSK2" >settime.out.$zone.2 2>&1
-  $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1
-  # Set key rollover relationship.
-  key_successor $KSK1 $KSK2
-  # Sign zone.
-  cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 6:
-  # The predecessor DNSKEY can be purged.
-  setup step6.ksk-doubleksk.$tld
-  # Subtract purge-keys interval from all the times (1h).
-  TpubN="now-1494h"
-  TactN="now-1467h"
-  TretN="now-54h"
-  TremN="now-4h"
-  TpubN1="now-81h"
-  TactN1="${TretN}"
-  TretN1="now+1386h"
-  TremN1="now+1440h"
-  ksktimes="-P ${TpubN}  -A ${TpubN}  -P sync ${TactN}  -I ${TretN}  -D ${TremN} -D sync ${TactN1}"
-  newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}"
-  zsktimes="-P ${TpubN}  -A ${TpubN}"
-  KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-  KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2)
-  ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3)
-  $SETTIME -s -g $H -k $H $TretN -r $H $TretN -d $H $TretN "$KSK1" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 "$KSK2" >settime.out.$zone.2 2>&1
-  $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1
-  # Set key rollover relationship.
-  key_successor $KSK1 $KSK2
-  # Sign zone.
-  cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-done
index 26653a6b534f0234a079852d03017e56144955c5..0eaa236b6d3401b37a241a2e3a3644b1b8fac209 100644 (file)
@@ -30,7 +30,11 @@ from rollover.common import (
     KSK_KEYTTLPROP,
     TIMEDELTA,
 )
-
+from rollover.setup import (
+    configure_root,
+    configure_tld,
+    configure_ksk_doubleksk,
+)
 
 CDSS = ["CDS (SHA-256)"]
 POLICY = "ksk-doubleksk"
@@ -48,6 +52,30 @@ OFFSETS["step6-p"] = OFFSETS["step5-p"] - int(KSK_CONFIG["purge-keys"].total_sec
 OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(KSK_CONFIG["purge-keys"].total_seconds())
 
 
+def bootstrap():
+    data = {
+        "tlds": [],
+        "trust_anchors": [],
+    }
+
+    tlds = []
+    for tld_name in [
+        "autosign",
+        "manual",
+    ]:
+        delegations = configure_ksk_doubleksk(tld_name)
+
+        tld = configure_tld(tld_name, delegations)
+        tlds.append(tld)
+
+        data["tlds"].append(tld_name)
+
+    ta = configure_root(tlds)
+    data["trust_anchors"].append(ta)
+
+    return data
+
+
 @pytest.mark.parametrize(
     "tld",
     [
index b3e5b2fa0609abc97bd12419f4749df7754e5b2d..e2f31c02a50a9204047197b401acb935061dca4d 100644 (file)
@@ -1353,3 +1353,270 @@ def configure_going_insecure(tld: str, reconfig: bool = False) -> List[Zone]:
             render_and_sign_zone(zonename, [ksk_name, zsk_name], extra_options="-P")
 
     return zones
+
+
+def configure_ksk_doubleksk(tld: str) -> List[Zone]:
+    # The zones at ksk-doubleksk.$tld represent the various steps of a KSK
+    # Double-KSK rollover.
+    zones = []
+    zone = f"ksk-doubleksk.{tld}"
+    cds = "cds:sha-256"
+    keygen = CmdHelper("KEYGEN", "-a ECDSAP256SHA256 -L 7200")
+    settime = CmdHelper("SETTIME", "-s")
+
+    # Step 1:
+    # Introduce the first key. This will immediately be active.
+    zonename = f"step1.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # Timing metadata.
+    TactN = "now-7d"
+    keytimes = f"-P {TactN} -A {TactN}"
+    # Key generation.
+    ksk_name = keygen(f"-f KSK {keytimes} {zonename}", cwd="ns3").strip()
+    zsk_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN} -r OMNIPRESENT {TactN} -d OMNIPRESENT {TactN} {ksk_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN} -z OMNIPRESENT {TactN} {zsk_name}",
+        cwd="ns3",
+    )
+    # Signing.
+    render_and_sign_zone(zonename, [ksk_name, zsk_name], extra_options=f"-G {cds}")
+
+    # Step 2:
+    # It is time to introduce the new KSK.
+    zonename = f"step2.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # Lksk:           60d
+    # Dreg:           n/a
+    # DprpC:          1h
+    # TTLds:          1d
+    # TTLkey:         2h
+    # publish-safety: 1d
+    # retire-safety:  2d
+    #
+    # According to RFC 7583:
+    # Tpub(N+1) <= Tact(N) + Lksk - Dreg - IpubC
+    # IpubC = DprpC + TTLkey (+publish-safety)
+    #
+    # IpubC   = 27h
+    # Tact(N) = now - Lksk + Dreg + IpubC = now - 60d + 27h
+    #         = now - 1440h + 27h = now - 1413h
+    TactN = "now-1413h"
+    keytimes = f"-P {TactN} -A {TactN}"
+    # Key generation.
+    ksk_name = keygen(f"-f KSK {keytimes} {zonename}", cwd="ns3").strip()
+    zsk_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN} -r OMNIPRESENT {TactN} -d OMNIPRESENT {TactN} {ksk_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN} -z OMNIPRESENT {TactN} {zsk_name}",
+        cwd="ns3",
+    )
+    # Signing.
+    render_and_sign_zone(zonename, [ksk_name, zsk_name], extra_options=f"-G {cds}")
+
+    # Step 3:
+    # It is time to submit the DS.
+    zonename = f"step3.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # According to RFC 7583:
+    # Iret = DprpP + TTLds (+retire-safety)
+    #
+    # Iret       = 50h
+    # Tpub(N)    = now - Lksk = now - 60d = now - 60d
+    # Tact(N)    = now - 1413h
+    # Tret(N)    = now
+    # Trem(N)    = now + Iret = now + 50h
+    # Tpub(N+1)  = now - IpubC = now - 27h
+    # Tact(N+1)  = now
+    # Tret(N+1)  = now + Lksk = now + 60d
+    # Trem(N+1)  = now + Lksk + Iret = now + 60d + 50h
+    #            = now + 1440h + 50h = 1490h
+    TpubN = "now-60d"
+    TactN = "now-1413h"
+    TretN = "now"
+    TremN = "now+50h"
+    TpubN1 = "now-27h"
+    TactN1 = "now"
+    TretN1 = "now+60d"
+    TremN1 = "now+1490h"
+    ksktimes = (
+        f"-P {TpubN} -A {TpubN} -P sync {TactN} -I {TretN} -D {TremN} -D sync {TactN1}"
+    )
+    newtimes = f"-P {TpubN1} -A {TactN1} -P sync {TactN1} -I {TretN1} -D {TremN1}"
+    zsktimes = f"-P {TpubN}  -A {TpubN}"
+    # Key generation.
+    ksk1_name = keygen(f"-f KSK {ksktimes} {zonename}", cwd="ns3").strip()
+    ksk2_name = keygen(f"-f KSK {newtimes} {zonename}", cwd="ns3").strip()
+    zsk_name = keygen(f"{zsktimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k OMNIPRESENT {TactN} -r OMNIPRESENT {TactN} -d OMNIPRESENT {TactN} {ksk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k RUMOURED {TpubN1} -r RUMOURED {TpubN1} -d HIDDEN {TpubN1} {ksk2_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TpubN} -z OMNIPRESENT {TpubN} {zsk_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(ksk1_name, ksk2_name)
+    # Signing.
+    render_and_sign_zone(
+        zonename, [ksk1_name, ksk2_name, zsk_name], extra_options=f"-G {cds}"
+    )
+
+    # Step 4:
+    # The DS should be swapped now.
+    zonename = f"step4.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # Tpub(N)    = now - Lksk - Iret = now - 60d - 50h
+    #            = now - 1440h - 50h = now - 1490h
+    # Tact(N)    = now - 1490h + 27h = now - 1463h
+    # Tret(N)    = now - Iret = now - 50h
+    # Trem(N)    = now
+    # Tpub(N+1)  = now - Iret - IpubC = now - 50h - 27h
+    #            = now - 77h
+    # Tact(N+1)  = Tret(N)
+    # Tret(N+1)  = now + Lksk - Iret = now + 60d - 50h = now + 1390h
+    # Trem(N+1)  = now + Lksk = now + 60d
+    TpubN = "now-1490h"
+    TactN = "now-1463h"
+    TretN = "now-50h"
+    TremN = "now"
+    TpubN1 = "now-77h"
+    TactN1 = TretN
+    TretN1 = "now+1390h"
+    TremN1 = "now+60d"
+    ksktimes = (
+        f"-P {TpubN} -A {TpubN} -P sync {TactN} -I {TretN} -D {TremN} -D sync {TactN1}"
+    )
+    newtimes = f"-P {TpubN1} -A {TactN1} -P sync {TactN1} -I {TretN1} -D {TremN1}"
+    zsktimes = f"-P {TpubN} -A {TpubN}"
+    # Key generation.
+    ksk1_name = keygen(f"-f KSK {ksktimes} {zonename}", cwd="ns3").strip()
+    ksk2_name = keygen(f"-f KSK {newtimes} {zonename}", cwd="ns3").strip()
+    zsk_name = keygen(f"{zsktimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k OMNIPRESENT {TactN} -r OMNIPRESENT {TactN} -d UNRETENTIVE {TretN} -D ds {TretN} {ksk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN1} -r OMNIPRESENT {TactN1} -d RUMOURED {TactN1} -P ds {TactN1} {ksk2_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN} -z OMNIPRESENT {TactN} {zsk_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(ksk1_name, ksk2_name)
+    # Signing.
+    render_and_sign_zone(
+        zonename, [ksk1_name, ksk2_name, zsk_name], extra_options=f"-G {cds}"
+    )
+
+    # Step 5:
+    # The predecessor DNSKEY is removed long enough that is has become HIDDEN.
+    zonename = f"step5.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # Subtract DNSKEY TTL + zone-propagation-delay from all the times (3h).
+    # Tpub(N)    = now - 1490h - 3h = now - 1493h
+    # Tact(N)    = now - 1463h - 3h = now - 1466h
+    # Tret(N)    = now - 50h - 3h = now - 53h
+    # Trem(N)    = now - 3h
+    # Tpub(N+1)  = now - 77h - 3h = now - 80h
+    # Tact(N+1)  = Tret(N)
+    # Tret(N+1)  = now + 1390h - 3h = now + 1387h
+    # Trem(N+1)  = now + 60d - 3h = now + 1441h
+    TpubN = "now-1493h"
+    TactN = "now-1466h"
+    TretN = "now-53h"
+    TremN = "now-3h"
+    TpubN1 = "now-80h"
+    TactN1 = TretN
+    TretN1 = "now+1387h"
+    TremN1 = "now+1441h"
+    ksktimes = (
+        f"-P {TpubN} -A {TpubN} -P sync {TactN} -I {TretN} -D {TremN} -D sync {TactN1}"
+    )
+    newtimes = f"-P {TpubN1} -A {TactN1} -P sync {TactN1} -I {TretN1} -D {TremN1}"
+    zsktimes = f"-P {TpubN} -A {TpubN}"
+    # Key generation.
+    ksk1_name = keygen(f"-f KSK {ksktimes} {zonename}", cwd="ns3").strip()
+    ksk2_name = keygen(f"-f KSK {newtimes} {zonename}", cwd="ns3").strip()
+    zsk_name = keygen(f"{zsktimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k UNRETENTIVE {TretN} -r UNRETENTIVE {TretN} -d HIDDEN {TretN} {ksk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN1} -r OMNIPRESENT {TactN1} -d OMNIPRESENT {TactN1} {ksk2_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN} -z OMNIPRESENT {TactN} {zsk_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(ksk1_name, ksk2_name)
+    # Signing.
+    render_and_sign_zone(
+        zonename, [ksk1_name, ksk2_name, zsk_name], extra_options=f"-G {cds}"
+    )
+
+    # Step 6:
+    # The predecessor DNSKEY can be purged.
+    zonename = f"step6.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # Subtract purge-keys interval from all the times (1h).
+    TpubN = "now-1494h"
+    TactN = "now-1467h"
+    TretN = "now-54h"
+    TremN = "now-4h"
+    TpubN1 = "now-81h"
+    TactN1 = TretN
+    TretN1 = "now+1386h"
+    TremN1 = "now+1440h"
+    ksktimes = (
+        f"-P {TpubN} -A {TpubN} -P sync {TactN} -I {TretN} -D {TremN} -D sync {TactN1}"
+    )
+    newtimes = f"-P {TpubN1} -A {TactN1} -P sync {TactN1} -I {TretN1} -D {TremN1}"
+    zsktimes = f"-P {TpubN} -A {TpubN}"
+    # Key generation.
+    ksk1_name = keygen(f"-f KSK {ksktimes} {zonename}", cwd="ns3").strip()
+    ksk2_name = keygen(f"-f KSK {newtimes} {zonename}", cwd="ns3").strip()
+    zsk_name = keygen(f"{zsktimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k HIDDEN {TretN} -r HIDDEN {TretN} -d HIDDEN {TretN} {ksk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN1} -r OMNIPRESENT {TactN1} -d OMNIPRESENT {TactN1} {ksk2_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN} -z OMNIPRESENT {TactN} {zsk_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(ksk1_name, ksk2_name)
+    # Signing.
+    render_and_sign_zone(
+        zonename, [ksk1_name, ksk2_name, zsk_name], extra_options=f"-G {cds}"
+    )
+
+    return zones