]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
rollover-csk-roll2: From setup.sh to pytest bootstrap
authorMatthijs Mekking <matthijs@isc.org>
Thu, 27 Nov 2025 13:36:07 +0000 (14:36 +0100)
committerMatthijs Mekking <matthijs@isc.org>
Fri, 19 Dec 2025 10:47:50 +0000 (11:47 +0100)
Similar to rollover-csk-roll1.

bin/tests/system/rollover-csk-roll2/ns1 [new symlink]
bin/tests/system/rollover-csk-roll2/ns2 [new symlink]
bin/tests/system/rollover-csk-roll2/ns3/kasp.conf [moved from bin/tests/system/rollover-csk-roll2/ns3/kasp.conf.j2 with 90% similarity]
bin/tests/system/rollover-csk-roll2/ns3/template.db.in [deleted symlink]
bin/tests/system/rollover-csk-roll2/ns3/template.db.j2.manual [new symlink]
bin/tests/system/rollover-csk-roll2/ns3/trusted.conf.j2 [new symlink]
bin/tests/system/rollover-csk-roll2/setup.sh [deleted file]
bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py
bin/tests/system/rollover/setup.py

diff --git a/bin/tests/system/rollover-csk-roll2/ns1 b/bin/tests/system/rollover-csk-roll2/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-csk-roll2/ns2 b/bin/tests/system/rollover-csk-roll2/ns2
new file mode 120000 (symlink)
index 0000000..41a09bb
--- /dev/null
@@ -0,0 +1 @@
+../rollover/ns2
\ No newline at end of file
similarity index 90%
rename from bin/tests/system/rollover-csk-roll2/ns3/kasp.conf.j2
rename to bin/tests/system/rollover-csk-roll2/ns3/kasp.conf
index cf1708f3eba478963a40414e8c4063d64a5a73cc..30b96679c34b14414ef352915bf145dc722b3102 100644 (file)
@@ -23,7 +23,7 @@ dnssec-policy "csk-roll2-autosign" {
 
        cds-digest-types { "sha-256"; "sha-384"; }; // use two digest type for testing purposes
        keys {
-               csk key-directory lifetime P6M algorithm @DEFAULT_ALGORITHM@;
+               csk key-directory lifetime P6M algorithm ecdsa256;
        };
 
        zone-propagation-delay PT1H;
@@ -47,7 +47,7 @@ dnssec-policy "csk-roll2-manual" {
 
        cds-digest-types { "sha-256"; "sha-384"; }; // use two digest type for testing purposes
        keys {
-               csk key-directory lifetime P6M algorithm @DEFAULT_ALGORITHM@;
+               csk key-directory lifetime P6M algorithm ecdsa256;
        };
 
        zone-propagation-delay PT1H;
diff --git a/bin/tests/system/rollover-csk-roll2/ns3/template.db.in b/bin/tests/system/rollover-csk-roll2/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-csk-roll2/ns3/template.db.j2.manual b/bin/tests/system/rollover-csk-roll2/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-csk-roll2/ns3/trusted.conf.j2 b/bin/tests/system/rollover-csk-roll2/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-csk-roll2/setup.sh b/bin/tests/system/rollover-csk-roll2/setup.sh
deleted file mode 100644 (file)
index da1e2d8..0000000
+++ /dev/null
@@ -1,301 +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 csk-roll2.$tld represent the various steps of a CSK rollover
-# (which is essentially a ZSK Pre-Publication / KSK Double-KSK rollover).
-# This scenario differs from the csk-roll1 one because the zone signatures (ZRRSIG)
-# are replaced with the new key sooner than the DS is swapped.
-#
-
-for tld in autosign manual; do
-  # Step 1:
-  # Introduce the first key. This will immediately be active.
-  setup step1.csk-roll2.$tld
-  TactN="now-7d"
-  keytimes="-P ${TactN} -A ${TactN}"
-  CSK=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
-  $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1
-  cat template.db.in "${CSK}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 2:
-  # It is time to introduce the new CSK.
-  setup step2.csk-roll2.$tld
-  # According to RFC 7583:
-  # KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC
-  # ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub
-  # IpubC = DprpC + TTLkey (+publish-safety)
-  # Ipub  = IpubC
-  # Lcsk = Lksk = Lzsk
-  #
-  # Lcsk:           6mo (186d, 4464h)
-  # Dreg:           N/A
-  # DprpC:          1h
-  # TTLkey:         1h
-  # publish-safety: 1h
-  # Ipub:           3h
-  #
-  # Tact(N)  = now - Lcsk + Ipub = now - 186d + 3h
-  #          = now - 4464h + 3h = now - 4461h
-  TactN="now-4461h"
-  keytimes="-P ${TactN} -A ${TactN}"
-  CSK=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
-  $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1
-  cat template.db.in "${CSK}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 3:
-  # It is time to submit the DS and to roll signatures.
-  setup step3.csk-roll2.$tld
-  # According to RFC 7583:
-  #
-  # Tsbm(N+1) >= Trdy(N+1)
-  # KSK: Tact(N+1) = Tsbm(N+1)
-  # ZSK: Tact(N+1) = Tpub(N+1) + Ipub = Tsbm(N+1)
-  # KSK: Iret  = DprpP + TTLds (+retire-safety)
-  # ZSK: IretZ = Dsgn + Dprp + TTLsig (+retire-safety)
-  #
-  # Lcsk:           186d
-  # Dprp:           1h
-  # DprpP:          1w
-  # Dreg:           N/A
-  # Dsgn:           12h
-  # TTLds:          1h
-  # TTLsig:         1d
-  # retire-safety:  1h
-  # Iret:           170h
-  # IretZ:          38h
-  # Ipub:           3h
-  #
-  # Tpub(N)   = now - Lcsk = now - 186d
-  # Tact(N)   = now - Lcsk + Dprp + TTLsig = now - 4439h
-  # Tret(N)   = now
-  # Trem(N)   = now + Iret = now + 170h
-  # Tpub(N+1) = now - Ipub = now - 3h
-  # Tact(N+1) = Tret(N)
-  # Tret(N+1) = now + Lcsk = now + 186d
-  # Trem(N+1) = now + Lcsk + Iret = now + 186d + 170h =
-  #           = now + 4464h + 170h = now + 4634h
-  TpubN="now-186d"
-  TactN="now-4439h"
-  TretN="now"
-  TremN="now+170h"
-  TpubN1="now-3h"
-  TactN1="${TretN}"
-  TretN1="now+186d"
-  TremN1="now+4634h"
-  keytimes="-P ${TpubN}  -P sync ${TactN}  -A ${TpubN}  -I ${TretN}  -D ${TremN} -D sync ${TactN1}"
-  newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}"
-  CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
-  CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2)
-  $SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK1" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 -z $H $TpubN1 "$CSK2" >settime.out.$zone.2 2>&1
-  # Set key rollover relationship.
-  key_successor $CSK1 $CSK2
-  # Sign zone.
-  cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 4:
-  # Some time later all the ZRRSIG records should be from the new CSK, and the
-  # DS should be swapped.  The ZRRSIG records are all replaced after IretZ (38h).
-  # The DS is swapped after Dreg + Iret (1w3h). In other words, the zone
-  # signatures are replaced before the DS is swapped.
-  setup step4.csk-roll2.$tld
-  # According to RFC 7583:
-  # Trem(N)    = Tret(N) + IretZ
-  #
-  # Lcsk:   186d
-  # Dreg:   N/A
-  # Iret:   170h
-  # IretZ:  38h
-  #
-  # Tpub(N)    = now - IretZ - Lcsk = now - 38h - 186d
-  #            = now - 38h - 4464h = now - 4502h
-  # Tact(N)    = now - Iret - Lcsk + TTLsig = now - 4502h + 25h = now - 4477h
-  # Tret(N)    = now - IretZ = now - 38h
-  # Trem(N)    = now - IretZ + Iret = now - 38h + 170h = now + 132h
-  # Tpub(N+1)  = now - IretZ - IpubC = now - 38h - 3h = now - 41h
-  # Tact(N+1)  = Tret(N)
-  # Tret(N+1)  = now - IretZ + Lcsk = now - 38h + 186d
-  #            = now + 4426h
-  # Trem(N+1)  = now - IretZ + Lcsk + Iret
-  #            = now + 4426h + 3h = now + 4429h
-  TpubN="now-4502h"
-  TactN="now-4477h"
-  TretN="now-38h"
-  TremN="now+132h"
-  TpubN1="now-41h"
-  TactN1="${TretN}"
-  TretN1="now+4426h"
-  TremN1="now+4429h"
-  keytimes="-P ${TpubN}  -P sync ${TactN}  -A ${TpubN}  -I ${TretN}  -D ${TremN} -D sync ${TactN1}"
-  newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}"
-  CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
-  CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2)
-  $SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $U $TretN -d $U $TactN1 -D ds $TactN1 "$CSK1" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -z $R $TactN1 -d $R $TactN1 -P ds $TactN1 "$CSK2" >settime.out.$zone.2 2>&1
-  # Set key rollover relationship.
-  key_successor $CSK1 $CSK2
-  # Sign zone.
-  cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 5:
-  # Some time later the DS can be swapped and the old DNSKEY can be removed from
-  # the zone.
-  setup step5.csk-roll2.$tld
-  # Subtract Iret (170h) - IretZ (38h) = 132h.
-  #
-  # Tpub(N)   = now - 4502h - 132h = now - 4634h
-  # Tact(N)   = now - 4477h - 132h = now - 4609h
-  # Tret(N)   = now - 38h - 132h = now - 170h
-  # Trem(N)   = now + 132h - 132h = now
-  # Tpub(N+1) = now - 41h - 132h = now - 173h
-  # Tact(N+1) = Tret(N)
-  # Tret(N+1) = now + 4426h - 132h = now + 4294h
-  # Trem(N+1) = now + 4492h - 132h = now + 4360h
-  TpubN="now-4634h"
-  TactN="now-4609h"
-  TretN="now-170h"
-  TremN="now"
-  TpubN1="now-173h"
-  TactN1="${TretN}"
-  TretN1="now+4294h"
-  TremN1="now+4360h"
-  keytimes="-P ${TpubN}  -P sync ${TactN}  -A ${TpubN}  -I ${TretN}  -D ${TremN} -D sync ${TactN1}"
-  newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}"
-  CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
-  CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2)
-  $SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $H now-133h -d $U $TactN1 -D ds $TactN1 "$CSK1" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -z $O now-133h -d $R $TactN1 -P ds $TactN1 "$CSK2" >settime.out.$zone.2 2>&1
-  # Set key rollover relationship.
-  key_successor $CSK1 $CSK2
-  # Sign zone.
-  cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 6:
-  # Some time later the predecessor DNSKEY enters the HIDDEN state.
-  setup step6.csk-roll2.$tld
-  # Subtract DNSKEY TTL plus zone propagation delay (2h).
-  #
-  # Tpub(N)   = now - 4634h - 2h = now - 4636h
-  # Tact(N)   = now - 4609h - 2h = now - 4611h
-  # Tret(N)   = now - 170h - 2h = now - 172h
-  # Trem(N)   = now - 2h
-  # Tpub(N+1) = now - 173h - 2h = now - 175h
-  # Tact(N+1) = Tret(N)
-  # Tret(N+1) = now + 4294h - 2h = now + 4292h
-  # Trem(N+1) = now + 4360h - 2h = now + 4358h
-  TpubN="now-4636h"
-  TactN="now-4611h"
-  TretN="now-172h"
-  TremN="now-2h"
-  TpubN1="now-175h"
-  TactN1="${TretN}"
-  TretN1="now+4292h"
-  TremN1="now+4358h"
-  keytimes="-P ${TpubN}  -P sync ${TactN}  -A ${TpubN}  -I ${TretN}  -D ${TremN} -D sync ${TactN1}"
-  newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}"
-  CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
-  CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2)
-  $SETTIME -s -g $H -k $U $TremN -r $U $TremN -d $H $TremN -z $H now-135h "$CSK1" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TremN -z $O now-135h "$CSK2" >settime.out.$zone.2 2>&1
-  # Set key rollover relationship.
-  key_successor $CSK1 $CSK2
-  # Sign zone.
-  cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-  # Step 7:
-  # The predecessor DNSKEY can be purged, but purge-keys is disabled.
-  setup step7.csk-roll2.$tld
-  # Subtract 90 days (default, 2160h) from all the times.
-  #
-  # Tpub(N)   = now - 4636h - 2160h = now - 6796h
-  # Tact(N)   = now - 4611h - 2160h = now - 6771h
-  # Tret(N)   = now - 172h - 2160h = now - 2332h
-  # Trem(N)   = now - 2h - 2160h = now - 2162h
-  # Tpub(N+1) = now - 175h - 2160h = now - 2335h
-  # Tact(N+1) = Tret(N)
-  # Tret(N+1) = now + 4292h - 2160h = now + 2132h
-  # Trem(N+1) = now + 4358h - 2160h = now + 2198h
-  TpubN="now-6796h"
-  TactN="now-6771h"
-  TretN="now-2332h"
-  TremN="now-2162h"
-  TpubN1="now-2335h"
-  TactN1="${TretN}"
-  TretN1="now+2132h"
-  TremN1="now+2198h"
-  keytimes="-P ${TpubN}  -P sync ${TactN}  -A ${TpubN}  -I ${TretN}  -D ${TremN} -D sync ${TactN1}"
-  newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}"
-  CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
-  CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2)
-  $SETTIME -s -g $H -k $U $TremN -r $U $TremN -d $H $TremN -z $H now-135h "$CSK1" >settime.out.$zone.1 2>&1
-  $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TremN -z $O now-135h "$CSK2" >settime.out.$zone.2 2>&1
-  # Set key rollover relationship.
-  key_successor $CSK1 $CSK2
-  # Sign zone.
-  cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile"
-  private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile"
-  cp $infile $zonefile
-  $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-done
index 5a3ae2205f4dd14ed9ccc90d2febec4975c9078a..529fd836eefdeccea0e3aa3f4b4131dadf3d689c 100644 (file)
@@ -24,7 +24,11 @@ from rollover.common import (
     size,
     TIMEDELTA,
 )
-
+from rollover.setup import (
+    configure_root,
+    configure_tld,
+    configure_cskroll2,
+)
 
 CDSS = ["CDNSKEY", "CDS (SHA-256)", "CDS (SHA-384)"]
 CONFIG = {
@@ -68,6 +72,30 @@ OFFSETS["step7-p"] = OFFSETS["step6-p"] - int(timedelta(days=90).total_seconds()
 OFFSETS["step7-s"] = OFFSETS["step6-s"] - int(timedelta(days=90).total_seconds())
 
 
+def bootstrap():
+    data = {
+        "tlds": [],
+        "trust_anchors": [],
+    }
+
+    tlds = []
+    for tld_name in [
+        "autosign",
+        "manual",
+    ]:
+        delegations = configure_cskroll2(tld_name, f"{POLICY}-{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 c2cacdb2009cb6d1a95ca6561e111c50edc452b4..2d7f178ca17da8cba44baf184758ee1fc4d46a1e 100644 (file)
@@ -528,6 +528,7 @@ def configure_cskroll1(tld: str, policy: str) -> List[Zone]:
     # (which is essentially a ZSK Pre-Publication / KSK Double-KSK rollover).
     zones = []
     zone = f"csk-roll1.{tld}"
+    cds = "cdnskey,cds:sha384"
     keygen = CmdHelper("KEYGEN", f"-k {policy} -l kasp.conf")
     settime = CmdHelper("SETTIME", "-s")
 
@@ -545,7 +546,7 @@ def configure_cskroll1(tld: str, policy: str) -> List[Zone]:
         cwd="ns3",
     )
     # Signing.
-    render_and_sign_zone(zonename, [csk_name], extra_options="-z -G cdnskey,cds:sha384")
+    render_and_sign_zone(zonename, [csk_name], extra_options=f"-z -G {cds}")
 
     # Step 2:
     # It is time to introduce the new CSK.
@@ -577,7 +578,7 @@ def configure_cskroll1(tld: str, policy: str) -> List[Zone]:
         cwd="ns3",
     )
     # Signing.
-    render_and_sign_zone(zonename, [csk_name], extra_options="-z -G cdnskey,cds:sha384")
+    render_and_sign_zone(zonename, [csk_name], extra_options=f"-z -G {cds}")
 
     # Step 3:
     # It is time to submit the DS and to roll signatures.
@@ -639,9 +640,7 @@ def configure_cskroll1(tld: str, policy: str) -> List[Zone]:
     # Set key rollover relationship.
     set_key_relationship(csk1_name, csk2_name)
     # Signing.
-    render_and_sign_zone(
-        zonename, [csk1_name, csk2_name], extra_options="-z -G cdnskey,cds:sha384"
-    )
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
 
     # Step 4:
     # Some time later all the ZRRSIG records should be from the new CSK, and the
@@ -694,9 +693,7 @@ def configure_cskroll1(tld: str, policy: str) -> List[Zone]:
     # Set key rollover relationship.
     set_key_relationship(csk1_name, csk2_name)
     # Signing.
-    render_and_sign_zone(
-        zonename, [csk1_name, csk2_name], extra_options="-z -G cdnskey,cds:sha384"
-    )
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
 
     # Step 5:
     # After the DS is swapped in step 4, also the KRRSIG records can be removed.
@@ -731,9 +728,7 @@ def configure_cskroll1(tld: str, policy: str) -> List[Zone]:
     # Set key rollover relationship.
     set_key_relationship(csk1_name, csk2_name)
     # Signing.
-    render_and_sign_zone(
-        zonename, [csk1_name, csk2_name], extra_options="-z -G cdnskey,cds:sha384"
-    )
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
 
     # Step 6:
     # After the retire interval has passed the predecessor DNSKEY can be
@@ -784,9 +779,7 @@ def configure_cskroll1(tld: str, policy: str) -> List[Zone]:
     # Set key rollover relationship.
     set_key_relationship(csk1_name, csk2_name)
     # Signing.
-    render_and_sign_zone(
-        zonename, [csk1_name, csk2_name], extra_options="-z -G cdnskey,cds:sha384"
-    )
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
 
     # Step 7:
     # Some time later the predecessor DNSKEY enters the HIDDEN state.
@@ -820,9 +813,7 @@ def configure_cskroll1(tld: str, policy: str) -> List[Zone]:
     # Set key rollover relationship.
     set_key_relationship(csk1_name, csk2_name)
     # Signing.
-    render_and_sign_zone(
-        zonename, [csk1_name, csk2_name], extra_options="-z -G cdnskey,cds:sha384"
-    )
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
 
     # Step 8:
     # The predecessor DNSKEY can be purged.
@@ -856,8 +847,350 @@ def configure_cskroll1(tld: str, policy: str) -> List[Zone]:
     # Set key rollover relationship.
     set_key_relationship(csk1_name, csk2_name)
     # Signing.
-    render_and_sign_zone(
-        zonename, [csk1_name, csk2_name], extra_options="-z -G cdnskey,cds:sha384"
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
+
+    return zones
+
+
+def configure_cskroll2(tld: str, policy: str) -> List[Zone]:
+    # The zones at csk-roll2.$tld represent the various steps of a CSK rollover
+    # (which is essentially a ZSK Pre-Publication / KSK Double-KSK rollover).
+    # This scenario differs from the csk-roll1 one because the zone signatures (ZRRSIG)
+    # are replaced with the new key sooner than the DS is swapped.
+    zones = []
+    zone = f"csk-roll2.{tld}"
+    cds = "cdnskey,cds:sha-256,cds:sha-384"
+    keygen = CmdHelper("KEYGEN", f"-k {policy} -l kasp.conf")
+    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}")
+    TactN = "now-7d"
+    keytimes = f"-P {TactN} -A {TactN}"
+    # Key generation.
+    csk_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN} -r OMNIPRESENT {TactN} -z OMNIPRESENT {TactN} -d OMNIPRESENT {TactN} {csk_name}",
+        cwd="ns3",
+    )
+    # Signing.
+    render_and_sign_zone(zonename, [csk_name], extra_options=f"-z -G {cds}")
+
+    # Step 2:
+    # It is time to introduce the new CSK.
+    zonename = f"step2.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # According to RFC 7583:
+    # KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC
+    # ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub
+    # IpubC = DprpC + TTLkey (+publish-safety)
+    # Ipub  = IpubC
+    # Lcsk = Lksk = Lzsk
+    #
+    # Lcsk:           6mo (186d, 4464h)
+    # Dreg:           N/A
+    # DprpC:          1h
+    # TTLkey:         1h
+    # publish-safety: 1h
+    # Ipub:           3h
+    #
+    # Tact(N)  = now - Lcsk + Ipub = now - 186d + 3h
+    #          = now - 4464h + 3h = now - 4461h
+    TactN = "now-4461h"
+    keytimes = f"-P {TactN} -A {TactN}"
+    # Key generation.
+    csk_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN} -r OMNIPRESENT {TactN} -z OMNIPRESENT {TactN} -d OMNIPRESENT {TactN} {csk_name}",
+        cwd="ns3",
+    )
+    # Signing.
+    render_and_sign_zone(zonename, [csk_name], extra_options=f"-z -G {cds}")
+
+    # Step 3:
+    # It is time to submit the DS and to roll signatures.
+    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:
+    #
+    # Tsbm(N+1) >= Trdy(N+1)
+    # KSK: Tact(N+1) = Tsbm(N+1)
+    # ZSK: Tact(N+1) = Tpub(N+1) + Ipub = Tsbm(N+1)
+    # KSK: Iret  = DprpP + TTLds (+retire-safety)
+    # ZSK: IretZ = Dsgn + Dprp + TTLsig (+retire-safety)
+    #
+    # Lcsk:           186d
+    # Dprp:           1h
+    # DprpP:          1w
+    # Dreg:           N/A
+    # Dsgn:           12h
+    # TTLds:          1h
+    # TTLsig:         1d
+    # retire-safety:  1h
+    # Iret:           170h
+    # IretZ:          38h
+    # Ipub:           3h
+    #
+    # Tpub(N)   = now - Lcsk = now - 186d
+    # Tact(N)   = now - Lcsk + Dprp + TTLsig = now - 4439h
+    # Tret(N)   = now
+    # Trem(N)   = now + IretZ = now + 26d3h = now + 627h
+    # Tpub(N+1) = now - Ipub = now - 3h
+    # Tact(N+1) = Tret(N)
+    # Tret(N+1) = now + Lcsk = now + 186d = now + 186d
+    # Trem(N+1) = now + Lcsk + IretZ = now + 186d + 26d3h =
+    #           = now + 5091h
+    TpubN = "now-186d"
+    TactN = "now-4439h"
+    TretN = "now"
+    TremN = "now+170h"
+    TpubN1 = "now-3h"
+    TactN1 = TretN
+    TretN1 = "now+186d"
+    TremN1 = "now+4634h"
+    keytimes = (
+        f"-P {TpubN} -P sync {TactN} -A {TpubN} -I {TretN} -D {TremN} -D sync {TactN1}"
+    )
+    newtimes = f"-P {TpubN1} -P sync {TactN1} -A {TactN1} -I {TretN1} -D {TremN1}"
+    # Key generation.
+    csk1_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    csk2_name = keygen(f"{newtimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k OMNIPRESENT {TactN} -r OMNIPRESENT {TactN} -z OMNIPRESENT {TactN} -d OMNIPRESENT {TactN} {csk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k RUMOURED {TpubN1} -r RUMOURED {TpubN1} -z HIDDEN {TpubN1} -d HIDDEN {TpubN1} {csk2_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(csk1_name, csk2_name)
+    # Signing.
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
+
+    # Step 4:
+    # Some time later all the ZRRSIG records should be from the new CSK, and the
+    # DS should be swapped.  The ZRRSIG records are all replaced after IretZ (38h).
+    # The DS is swapped after Dreg + Iret (1w3h). In other words, the zone
+    # signatures are replaced before the DS is swapped.
+    zonename = f"step4.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # According to RFC 7583:
+    # Trem(N)    = Tret(N) + IretZ
+    #
+    # Lcsk:   186d
+    # Dreg:   N/A
+    # Iret:   170h
+    # IretZ:  38h
+    #
+    # Tpub(N)    = now - IretZ - Lcsk = now - 38h - 186d
+    #            = now - 38h - 4464h = now - 4502h
+    # Tact(N)    = now - Iret - Lcsk + TTLsig = now - 4502h + 25h = now - 4477h
+    # Tret(N)    = now - IretZ = now - 38h
+    # Trem(N)    = now - IretZ + Iret = now - 38h + 170h = now + 132h
+    # Tpub(N+1)  = now - IretZ - IpubC = now - 38h - 3h = now - 41h
+    # Tact(N+1)  = Tret(N)
+    # Tret(N+1)  = now - IretZ + Lcsk = now - 38h + 186d
+    #            = now + 4426h
+    # Trem(N+1)  = now - IretZ + Lcsk + Iret
+    #            = now + 4426h + 3h = now + 4429h
+    TpubN = "now-4502h"
+    TactN = "now-4477h"
+    TretN = "now-38h"
+    TremN = "now+132h"
+    TpubN1 = "now-41h"
+    TactN1 = TretN
+    TretN1 = "now+4426h"
+    TremN1 = "now+4429h"
+    keytimes = (
+        f"-P {TpubN} -P sync {TactN} -A {TpubN} -I {TretN} -D {TremN} -D sync {TactN1}"
+    )
+    newtimes = f"-P {TpubN1} -P sync {TactN1} -A {TactN1} -I {TretN1} -D {TremN1}"
+    # Key generation.
+    csk1_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    csk2_name = keygen(f"{newtimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k OMNIPRESENT {TactN} -r OMNIPRESENT {TactN} -z UNRETENTIVE {TretN} -d UNRETENTIVE {TretN} -D ds {TretN} {csk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN1} -r OMNIPRESENT {TactN1} -z RUMOURED {TactN1} -d RUMOURED {TactN1} -P ds {TactN1} {csk2_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(csk1_name, csk2_name)
+    # Signing.
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
+
+    # Step 5:
+    # Some time later the DS can be swapped and the old DNSKEY can be removed from
+    # the zone.
+    zonename = f"step5.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # Subtract Iret (170h) - IretZ (38h) = 132h.
+    #
+    # Tpub(N)   = now - 4502h - 132h = now - 4634h
+    # Tact(N)   = now - 4477h - 132h = now - 4609h
+    # Tret(N)   = now - 38h - 132h = now - 170h
+    # Trem(N)   = now + 132h - 132h = now
+    # Tpub(N+1) = now - 41h - 132h = now - 173h
+    # Tact(N+1) = Tret(N)
+    # Tret(N+1) = now + 4426h - 132h = now + 4294h
+    # Trem(N+1) = now + 4492h - 132h = now + 4360h
+    TpubN = "now-4634h"
+    TactN = "now-4609h"
+    TretN = "now-170h"
+    TremN = "now"
+    TpubN1 = "now-173h"
+    TactN1 = TretN
+    TretN1 = "now+4294h"
+    TremN1 = "now+4360h"
+    keytimes = (
+        f"-P {TpubN} -P sync {TactN} -A {TpubN} -I {TretN} -D {TremN} -D sync {TactN1}"
+    )
+    newtimes = f"-P {TpubN1} -P sync {TactN1} -A {TactN1} -I {TretN1} -D {TremN1}"
+    # Key generation.
+    csk1_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    csk2_name = keygen(f"{newtimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k OMNIPRESENT {TactN} -r OMNIPRESENT {TactN} -z HIDDEN now-133h -d UNRETENTIVE {TactN1} -D ds {TactN1} {csk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN1} -r OMNIPRESENT {TactN1} -z OMNIPRESENT now-133h -d RUMOURED {TactN1} -P ds {TactN1} {csk2_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(csk1_name, csk2_name)
+    # Signing.
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
+
+    # Step 6:
+    # Some time later the predecessor DNSKEY enters the HIDDEN state.
+    zonename = f"step6.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # Subtract DNSKEY TTL plus zone propagation delay (2h).
+    #
+    # Tpub(N)   = now - 4634h - 2h = now - 4636h
+    # Tact(N)   = now - 4609h - 2h = now - 4611h
+    # Tret(N)   = now - 170h - 2h = now - 172h
+    # Trem(N)   = now - 2h
+    # Tpub(N+1) = now - 173h - 2h = now - 175h
+    # Tact(N+1) = Tret(N)
+    # Tret(N+1) = now + 4294h - 2h = now + 4292h
+    # Trem(N+1) = now + 4360h - 2h = now + 4358h
+    TpubN = "now-4636h"
+    TactN = "now-4611h"
+    TretN = "now-172h"
+    TremN = "now-2h"
+    TpubN1 = "now-175h"
+    TactN1 = TretN
+    TretN1 = "now+4292h"
+    TremN1 = "now+4358h"
+    keytimes = (
+        f"-P {TpubN} -P sync {TactN} -A {TpubN} -I {TretN} -D {TremN} -D sync {TactN1}"
     )
+    newtimes = f"-P {TpubN1} -P sync {TactN1} -A {TactN1} -I {TretN1} -D {TremN1}"
+    # Key generation.
+    csk1_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    csk2_name = keygen(f"{newtimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k UNRETENTIVE {TremN} -r UNRETENTIVE {TremN} -z HIDDEN now-135h -d HIDDEN {TremN} {csk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN1} -r OMNIPRESENT {TactN1} -z OMNIPRESENT now-135h -d OMNIPRESENT {TremN} {csk2_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(csk1_name, csk2_name)
+    # Signing.
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
+
+    # Step 7:
+    # The predecessor DNSKEY can be purged, but purge-keys is disabled.
+    zonename = f"step7.{zone}"
+    zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")))
+    isctest.log.info(f"setup {zonename}")
+    # Subtract 90 days (default, 2160h) from all the times.
+    #
+    # Tpub(N)   = now - 4636h - 2160h = now - 6796h
+    # Tact(N)   = now - 4611h - 2160h = now - 6771h
+    # Tret(N)   = now - 172h - 2160h = now - 2332h
+    # Trem(N)   = now - 2h - 2160h = now - 2162h
+    # Tpub(N+1) = now - 175h - 2160h = now - 2335h
+    # Tact(N+1) = Tret(N)
+    # Tret(N+1) = now + 4292h - 2160h = now + 2132h
+    # Trem(N+1) = now + 4358h - 2160h = now + 2198h
+    TpubN = "now-6796h"
+    TactN = "now-6771h"
+    TretN = "now-2332h"
+    TremN = "now-2162h"
+    TpubN1 = "now-2335h"
+    TactN1 = TretN
+    TretN1 = "now+2132h"
+    TremN1 = "now+2198h"
+
+    keytimes = (
+        f"-P {TpubN} -P sync {TactN} -A {TpubN} -I {TretN} -D {TremN} -D sync {TactN1}"
+    )
+    newtimes = f"-P {TpubN1} -P sync {TactN1} -A {TactN1} -I {TretN1} -D {TremN1}"
+    # Key generation.
+    csk1_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    csk2_name = keygen(f"{newtimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k UNRETENTIVE {TremN} -r HIDDEN {TremN} -z HIDDEN {TactN1} -d HIDDEN {TremN} {csk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN1} -r OMNIPRESENT {TactN1} -z OMNIPRESENT {TactN1} -d OMNIPRESENT {TremN} {csk2_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(csk1_name, csk2_name)
+    # Signing.
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
+
+    # Step 8:
+    # The predecessor DNSKEY can be purged.
+    zonename = f"step8.{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-5094h"
+    TactN = "now-5069h"
+    TretN = "now-630h"
+    TremN = "now-3h"
+    TpubN1 = "now-633h"
+    TactN1 = TretN
+    TretN1 = "now+3834h"
+    TremN1 = "now+4461h"
+    keytimes = (
+        f"-P {TpubN} -P sync {TactN} -A {TpubN} -I {TretN} -D {TremN} -D sync {TactN1}"
+    )
+    newtimes = f"-P {TpubN1} -P sync {TactN1} -A {TactN1} -I {TretN1} -D {TremN1}"
+    # Key generation.
+    csk1_name = keygen(f"{keytimes} {zonename}", cwd="ns3").strip()
+    csk2_name = keygen(f"{newtimes} {zonename}", cwd="ns3").strip()
+    settime(
+        f"-g HIDDEN -k UNRETENTIVE {TremN} -r UNRETENTIVE {TremN} -z HIDDEN now-2295h -d HIDDEN {TremN} {csk1_name}",
+        cwd="ns3",
+    )
+    settime(
+        f"-g OMNIPRESENT -k OMNIPRESENT {TactN1} -r OMNIPRESENT {TactN1} -z OMNIPRESENT now-2295h -d OMNIPRESENT {TremN} {csk2_name}",
+        cwd="ns3",
+    )
+    # Set key rollover relationship.
+    set_key_relationship(csk1_name, csk2_name)
+    # Signing.
+    render_and_sign_zone(zonename, [csk1_name, csk2_name], extra_options=f"-z -G {cds}")
 
     return zones