]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Test ZSK and KSK rollover
authorMatthijs Mekking <matthijs@isc.org>
Thu, 17 Oct 2019 13:50:52 +0000 (15:50 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Wed, 6 Nov 2019 21:36:21 +0000 (22:36 +0100)
Add tests for ZSK Pre-Publication and KSK Double-KSK rollover.

Includes tests for next key event is scheduled at the right time.

bin/tests/system/kasp/ns3/named.conf.in
bin/tests/system/kasp/ns3/policies/autosign.conf [new file with mode: 0644]
bin/tests/system/kasp/ns3/setup.sh
bin/tests/system/kasp/tests.sh

index 880f3fd50c7388a9263df7ca928f792b5cf18d2a..c7a830dc3ab31da83d767fef0f52c2e2ad38a3d6 100644 (file)
@@ -33,6 +33,7 @@ controls {
 };
 
 include "policies/kasp.conf";
+include "policies/autosign.conf";
 
 /* Zones that are getting initially signed */
 
@@ -120,3 +121,115 @@ zone "ecdsa384.kasp" {
        file "ecdsa384.kasp.db";
        dnssec-policy "ecdsa384";
 };
+
+/*
+ * Zones in different signing states.
+ */
+
+/*
+ * Zone that has expired signatures.
+ */
+zone "expired-sigs.autosign" {
+       type master;
+       file "expired-sigs.autosign.db";
+       dnssec-policy "autosign";
+};
+
+/*
+ * Zone that has valid, fresh signatures.
+ */
+zone "fresh-sigs.autosign" {
+       type master;
+       file "fresh-sigs.autosign.db";
+       dnssec-policy "autosign";
+};
+
+/*
+ * Zone that has unfresh signatures.
+ */
+zone "unfresh-sigs.autosign" {
+       type master;
+       file "unfresh-sigs.autosign.db";
+       dnssec-policy "autosign";
+};
+
+/*
+ * Zone that has missing private ZSK.
+ */
+zone "zsk-missing.autosign" {
+       type master;
+       file "zsk-missing.autosign.db";
+       dnssec-policy "autosign";
+};
+
+/*
+ * Zone that has inactive ZSK.
+ */
+zone "zsk-retired.autosign" {
+       type master;
+       file "zsk-retired.autosign.db";
+       dnssec-policy "autosign";
+};
+
+/*
+ * Zones for testing ZSK Pre-Publication steps.
+ */
+zone "step1.zsk-prepub.autosign" {
+       type master;
+       file "step1.zsk-prepub.autosign.db";
+       dnssec-policy "zsk-prepub";
+};
+zone "step2.zsk-prepub.autosign" {
+       type master;
+       file "step2.zsk-prepub.autosign.db";
+       dnssec-policy "zsk-prepub";
+};
+zone "step3.zsk-prepub.autosign" {
+       type master;
+       file "step3.zsk-prepub.autosign.db";
+       dnssec-policy "zsk-prepub";
+};
+zone "step4.zsk-prepub.autosign" {
+       type master;
+       file "step4.zsk-prepub.autosign.db";
+       dnssec-policy "zsk-prepub";
+};
+zone "step5.zsk-prepub.autosign" {
+       type master;
+       file "step5.zsk-prepub.autosign.db";
+       dnssec-policy "zsk-prepub";
+};
+
+/*
+ * Zones for testing KSK Double-KSK steps.
+ */
+zone "step1.ksk-doubleksk.autosign" {
+       type master;
+       file "step1.ksk-doubleksk.autosign.db";
+       dnssec-policy "ksk-doubleksk";
+};
+zone "step2.ksk-doubleksk.autosign" {
+       type master;
+       file "step2.ksk-doubleksk.autosign.db";
+       dnssec-policy "ksk-doubleksk";
+};
+zone "step3.ksk-doubleksk.autosign" {
+       type master;
+       file "step3.ksk-doubleksk.autosign.db";
+       dnssec-policy "ksk-doubleksk";
+};
+zone "step4.ksk-doubleksk.autosign" {
+       type master;
+       file "step4.ksk-doubleksk.autosign.db";
+       dnssec-policy "ksk-doubleksk";
+};
+zone "step5.ksk-doubleksk.autosign" {
+       type master;
+       file "step5.ksk-doubleksk.autosign.db";
+       dnssec-policy "ksk-doubleksk";
+};
+zone "step6.ksk-doubleksk.autosign" {
+       type master;
+       file "step6.ksk-doubleksk.autosign.db";
+       dnssec-policy "ksk-doubleksk";
+};
diff --git a/bin/tests/system/kasp/ns3/policies/autosign.conf b/bin/tests/system/kasp/ns3/policies/autosign.conf
new file mode 100644 (file)
index 0000000..3a0d028
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+dnssec-policy "autosign" {
+
+       signatures-refresh P1W;
+       signatures-validity P2W;
+       signatures-validity-dnskey P2W;
+
+       dnskey-ttl 300;
+
+       keys {
+               ksk key-directory P2Y 13;
+               zsk key-directory P1Y 13;
+       };
+};
+
+dnssec-policy "zsk-prepub" {
+
+       signatures-refresh P1W;
+       signatures-validity P2W;
+       signatures-validity-dnskey P2W;
+
+       dnskey-ttl 3600;
+       publish-safety P1D;
+       retire-safety P2D;
+
+       keys {
+               ksk key-directory P2Y 13;
+               zsk key-directory P30D 13;
+       };
+
+       zone-propagation-delay PT1H;
+       zone-max-ttl 1d;
+};
+
+dnssec-policy "ksk-doubleksk" {
+
+       signatures-refresh P1W;
+       signatures-validity P2W;
+       signatures-validity-dnskey P2W;
+
+       dnskey-ttl 2h;
+       publish-safety P1D;
+       retire-safety P2D;
+
+       keys {
+               ksk key-directory P60D 13;
+               zsk key-directory P1Y 13;
+       };
+
+       zone-propagation-delay PT1H;
+       zone-max-ttl 1d;
+
+       parent-ds-ttl 3600;
+       parent-registration-delay P1D;
+       parent-propagation-delay PT1H;
+};
index b2fcaa7edbc475b2d6e4253cf0de647ed40c39c4..b384fa730bdf90d5cf6b8c1c34093e2c8a14881b 100644 (file)
@@ -22,6 +22,23 @@ setup() {
        echo $zone >> zones
 }
 
+private_type_record() {
+       _zone=$1
+       _algorithm=$2
+       _keyfile=$3
+
+       _id=$(keyfile_to_key_id "$_keyfile")
+
+       printf "%s. 0 IN TYPE65534 \# 5 %02x%04x0000\n" $_zone $_algorithm $_id
+}
+
+
+# Make lines shorter by storing key states in environment variables.
+H="HIDDEN"
+R="RUMOURED"
+O="OMNIPRESENT"
+U="UNRETENTIVE"
+
 #
 # Set up zones that will be initially signed.
 #
@@ -48,3 +65,294 @@ zone="pregenerated.kasp"
 $KEYGEN -k rsasha1 -l policies/kasp.conf $zone > keygen.out.$zone.1 2>&1
 $KEYGEN -k rsasha1 -l policies/kasp.conf $zone > keygen.out.$zone.2 2>&1
 
+#
+# Set up zones that are already signed.
+#
+
+# These signatures are set to expire long in the past, update immediately.
+setup expired-sigs.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 300 $zone 2> keygen.out.$zone.1`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 300 $zone 2> keygen.out.$zone.2`
+T="now-6mo"
+$SETTIME -s -P $T -A $T  -g $O  -d $O $T -k $O $T -r $O $T $KSK > settime.out.$zone.1 2>&1
+$SETTIME -s -P $T -A $T  -g $O  -k $O $T -z $O $T $ZSK > settime.out.$zone.2 2>&1
+cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
+private_type_record $zone 13 $KSK >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -PS -x -s now-2mo -e now-1mo -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# These signatures are still good, and can be reused.
+setup fresh-sigs.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 300 $zone 2> keygen.out.$zone.1`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 300 $zone 2> keygen.out.$zone.2`
+T="now-6mo"
+$SETTIME -s -P $T -A $T  -g $O  -d $O $T -k $O $T -r $O $T $KSK > settime.out.$zone.1 2>&1
+$SETTIME -s -P $T -A $T  -g $O  -k $O $T -z $O $T $ZSK > settime.out.$zone.2 2>&1
+cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
+private_type_record $zone 13 $KSK >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# These signatures are still good, but not fresh enough, update immediately.
+setup unfresh-sigs.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 300 $zone 2> keygen.out.$zone.1`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 300 $zone 2> keygen.out.$zone.2`
+T="now-6mo"
+$SETTIME -s -P $T -A $T  -g $O  -d $O $T -k $O $T -r $O $T $KSK > settime.out.$zone.1 2>&1
+$SETTIME -s -P $T -A $T  -g $O  -k $O $T -z $O $T $ZSK > settime.out.$zone.2 2>&1
+cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
+private_type_record $zone 13 $KSK >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -S -x -s now-1w -e now+1w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# These signatures are already expired, and the private ZSK is missing.
+setup zsk-missing.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 300 $zone 2> keygen.out.$zone.1`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 300 $zone 2> keygen.out.$zone.2`
+T="now-6mo"
+$SETTIME -s -P $T -A $T  -g $O  -d $O $T -k $O $T -r $O $T $KSK > settime.out.$zone.1 2>&1
+$SETTIME -s -P $T -A $T  -g $O  -k $O $T -z $O $T $ZSK > settime.out.$zone.2 2>&1
+cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
+private_type_record $zone 13 $KSK >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -PS -x -s now-2w -e now-1mi -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+rm -f ${ZSK}.private
+
+# These signatures are already expired, and the private ZSK is retired.
+setup zsk-retired.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 300 $zone 2> keygen.out.$zone.1`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 300 $zone 2> keygen.out.$zone.2`
+T="now-6mo"
+$SETTIME -s -P $T -A $T  -g $O  -d $O $T -k $O $T -r $O $T $KSK > settime.out.$zone.1 2>&1
+$SETTIME -s -P $T -A $T  -g $O  -k $O $T -z $O $T $ZSK > settime.out.$zone.2 2>&1
+cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
+private_type_record $zone 13 $KSK >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -PS -x -s now-2w -e now-1mi -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+$SETTIME -s -I now -g HIDDEN $ZSK > settime.out.$zone.3 2>&1
+
+#
+# The zones at zsk-prepub.autosign represent the various steps of a ZSK
+# Pre-Publication rollover.
+#
+
+# Step 1:
+# Introduce the first key. This will immediately be active.
+setup step1.zsk-prepub.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2`
+TactN="now"
+$SETTIME -s -P $TactN -A $TactN  -g $O -k $O $TactN -r $O $TactN -d $O $TactN $KSK > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN -A $TactN  -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 13 $KSK >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# Step 2:
+# It is time to pre-publish the successor ZSK.
+setup step2.zsk-prepub.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2`
+# According to RFC 7583: Tpub(N+1) <= Tact(N) + Lzsk - Ipub
+# Also: Ipub = Dprp + TTLkey (+publish-safety)
+# so:   Tact(N) = Tpub(N+1) + Ipub - Lzsk = now + (1d2h) - 30d =
+#       now + 26h - 30d = now − 694h
+TactN="now-694h"
+$SETTIME -s -P $TactN -A $TactN  -g $O -k $O $TactN -r $O $TactN -d $O $TactN $KSK > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN -A $TactN  -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 13 $KSK >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# Step 3:
+# After the publication interval has passed the DNSKEY of the successor ZSK
+# is OMNIPRESENT and the zone can thus be signed with the successor ZSK.
+setup step3.zsk-prepub.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1`
+ZSK1=`$KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2`
+ZSK2=`$KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.3`
+# According to RFC 7583: Tpub(N+1) <= Tact(N) + Lzsk - Ipub
+# Also: Tret(N) = Tact(N+1) = Tact(N) + Lzsk
+# so:   Tact(N) = Tact(N+1) - Lzsk = now - 30d
+# and:  Tpub(N+1) = Tact(N+1) - Ipub = now - 26h
+# and:  Tret(N+1) = Tact(N+1) + Lzsk
+TactN="now-30d"
+TpubN1="now-26h"
+TretN1="now+30d"
+$SETTIME -s -P $TactN  -A $TactN            -g $O -k $O $TactN  -r $O $TactN -d $O $TactN $KSK  > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN  -A $TactN -I now     -g $H -k $O $TactN  -z $O $TactN              $ZSK1 > settime.out.$zone.2 2>&1
+$SETTIME -s -S $ZSK1             -i 0                                                     $ZSK2 > settime.out.$zone.3 2>&1
+$SETTIME -s -P $TpubN1 -A now    -I $TretN1 -g $O -k $R $TpubN1 -z $H $TpubN1             $ZSK2 > settime.out.$zone.4 2>&1
+cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" > "$infile"
+private_type_record $zone 13 $KSK  >> "$infile"
+private_type_record $zone 13 $ZSK1 >> "$infile"
+private_type_record $zone 13 $ZSK2 >> "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# Step 4:
+# After the retire interval has passed the predecessor DNSKEY can be
+# removed from the zone.
+setup step4.zsk-prepub.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1`
+ZSK1=`$KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2`
+ZSK2=`$KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.3`
+# According to RFC 7583: Tret(N) = Tact(N) + Lzsk
+# Also: Tdea(N) = Tret(N) + Iret
+# Also: Iret = Dsgn + Dprp + TTLsig (+retire-safety)
+# so:   Tact(N) = Tdea(N) - Iret - Lzsk = now - (1w1h1d2d) - 30d =
+#       now - (10d1h) - 30d = now - 961h
+# and:  Tret(N) = Tdea(N) - Iret = now - (10d1h) = now - 241h
+# and:  Tpub(N+1) = Tdea(N) - Iret - Ipub = now - (10d1h) - 26h =
+#       now - 267h
+# and:  Tact(N+1) = Tdea(N) - Iret = Tret(N)
+# and:  Tret(N+1) = Tdea(N) - Iret + Lzsk = now - (10d1h) + 30d =
+#       now + 479h
+TactN="now-961h"
+TretN="now-241h"
+TpubN1="now-267h"
+TactN1="${TretN}"
+TretN1="now+479h"
+$SETTIME -s -P $TactN  -A $TactN             -g $O -k $O $TactN  -r $O $TactN -d $O $TactN $KSK  > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN  -A $TactN  -I $TretN  -g $H -k $O $TactN  -z $U $TretN              $ZSK1 > settime.out.$zone.2 2>&1
+$SETTIME -s -S $ZSK1              -i 0                                                     $ZSK2 > settime.out.$zone.3 2>&1
+$SETTIME -s -P $TpubN1 -A $TactN1 -I $TretN1 -g $O -k $O $TactN1 -z $R $TactN1             $ZSK2 > settime.out.$zone.4 2>&1
+cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" > "$infile"
+$SIGNER -PS -x -s now-2w -e now-1mi -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# Step 5:
+# The predecessor DNSKEY is removed long enough that is has become HIDDEN.
+setup step5.zsk-prepub.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1`
+ZSK1=`$KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2`
+ZSK2=`$KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.3`
+# Substract DNSKEY TTL from all the times (1h).
+TactN="now-962h"
+TretN="now-242h"
+TpubN1="now-268h"
+TactN1="${TretN}"
+TretN1="now+478h"
+$SETTIME -s -P $TactN  -A $TactN                   -g $O -k $O $TactN  -r $O $TactN -d $O $TactN $KSK  > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN  -A $TactN  -I $TretN -D now -g $H -k $U $TretN  -z $U $TretN              $ZSK1 > settime.out.$zone.2 2>&1
+$SETTIME -s -S $ZSK1              -i 0                                                           $ZSK2 > settime.out.$zone.3 2>&1
+$SETTIME -s -P $TpubN1 -A $TactN1 -I $TretN1       -g $O -k $O $TactN1 -z $R $TactN1             $ZSK2 > settime.out.$zone.4 2>&1
+cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" > "$infile"
+private_type_record $zone 13 $KSK  >> "$infile"
+private_type_record $zone 13 $ZSK1 >> "$infile"
+private_type_record $zone 13 $ZSK2 >> "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+#
+# The zones at ksk-doubleksk.autosign represent the various steps of a KSK
+# Double-KSK rollover.
+#
+
+# Step 1:
+# Introduce the first key. This will immediately be active.
+setup step1.ksk-doubleksk.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 7200 $zone 2> keygen.out.$zone.1`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 7200 $zone 2> keygen.out.$zone.2`
+TactN="now"
+$SETTIME -s -P $TactN -A $TactN -g $O -k $O $TactN -r $O $TactN -d $O $TactN $KSK > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN -A $TactN -g $O              -k $O $TactN -z $O $TactN $ZSK > settime.out.$zone.2 2>&1
+cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# Step 2:
+# It is time to submit the introduce the new KSK.
+setup step2.ksk-doubleksk.autosign
+KSK=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 7200 $zone 2> keygen.out.$zone.1`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 7200 $zone 2> keygen.out.$zone.2`
+# According to RFC 7583: Tpub(N+1) <= Tact(N) + Lksk - Dreg - IpubC
+# Also: IpubC = DprpC + TTLkey (+publish-safety)
+# so:   Tact(N) = Tpub(N+1) - Lksk + Dreg + IpubC = now - 60d + (1d3h)
+#       now - 1440h + 27h = now - 1413h
+TactN="now-1413h"
+$SETTIME -s -P $TactN -A $TactN -g $O -k $O $TactN -r $O $TactN -d $O $TactN $KSK > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN -A $TactN -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 13 $KSK >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# Step 3:
+# It is time to submit the DS.
+setup step3.ksk-doubleksk.autosign
+KSK1=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 7200 $zone 2> keygen.out.$zone.1`
+KSK2=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 7200 $zone 2> keygen.out.$zone.2`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 7200 $zone 2> keygen.out.$zone.3`
+# According to RFC 7583: Tsbm(N+1) >= Trdy(N+1)
+# Also: Tact(N+1) = Tsbm(N+1) + Dreg
+# so:   Tact(N) = Tsbm(N+1) + Dreg - Lksk = now + 1d - 60d = now - 59d
+# and:  Tret(N) = Tsbm(N+1) + Dreg = now + 1d
+# and:  Tpub(N+1) <= Tsbm(N+1) - IpubC = now + 27h
+# and:  Tret(N+1) = Tsbm(N+1) + Dreg + Lksk = 1d + 60d
+TactN="now-59d"
+TretN="now+1d"
+TpubN1="now-27h"
+TretN1="now+61d"
+$SETTIME -s -P $TactN  -A $TactN  -I $TretN  -g $H -k $O $TactN   -r $O $TactN  -d $O $TactN  $KSK1 > settime.out.$zone.1 2>&1
+$SETTIME -s -S $KSK1              -i 0                                                        $KSK2 > settime.out.$zone.3 2>&1
+$SETTIME -s -P $TpubN1 -A $TretN  -I $TretN1 -g $O -k $R $TpubN1  -r $R $TpubN1 -d $H $TpubN1 $KSK2 > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN  -A $TactN             -g $O -k $O $TactN   -z $O $TactN                $ZSK  > settime.out.$zone.2 2>&1
+cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" > "$infile"
+private_type_record $zone 13 $KSK1 >> "$infile"
+private_type_record $zone 13 $KSK2 >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
+
+# Step 4:
+# The DS should be swapped now.
+setup step4.ksk-doubleksk.autosign
+KSK1=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 7200 $zone 2> keygen.out.$zone.1`
+KSK2=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 7200 $zone 2> keygen.out.$zone.2`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 7200 $zone 2> keygen.out.$zone.3`
+# According to RFC 7583: Tdea(N) = Tret(N) + Iret
+# Also: Tret(N) = Tsbm(N+1) + Dreg
+# Also: Tact(N+1) = Tret(N)
+# Also: Iret = DprpP + TTLds (+retire-safety)
+# so:   Tact(N) = Tdea(N) - Lksk - Iret = now - 60d - 2d2h = now - 1490h
+# and:  Tret(N) = Tdea(N) - Iret = now - 2d2h = 50h
+# and:  Tpub(N+1) = Tdea(N) - Iret - Dreg - IpubC = now - 50h - 1d - 1d3h = now - 101h
+# and:  Tsbm(N+1) = Tdea(N) - Iret - Dreg = now - 50h - 1d = now - 74h
+# and:  Tact(N+1) = Tret(N)
+# and:  Tret(N+1) = Tdea(N) + Lksk - Iret = now + 60d - 2d2h = now + 1390h
+TactN="now-1490h"
+TretN="now-50h"
+TpubN1="now-101h"
+TsbmN1="now-74h"
+TactN1="${TretN}"
+TretN1="now+1390h"
+$SETTIME -s -P $TactN  -A $TactN  -I $TretN  -g $H -k $O $TactN  -r $O $TactN  -d $U $TsbmN1 $KSK1 > settime.out.$zone.1 2>&1
+$SETTIME -s -S $KSK1              -i 0                                                       $KSK2 > settime.out.$zone.3 2>&1
+$SETTIME -s -P $TpubN1 -A $TactN1 -I $TretN1 -g $O -k $O $TsbmN1 -r $O $TsbmN1 -d $R $TsbmN1 $KSK2 > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN  -A $TactN             -g $O -k $O $TactN  -z $O $TactN                $ZSK  > settime.out.$zone.2 2>&1
+cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" > "$infile"
+private_type_record $zone 13 $KSK1 >> "$infile"
+private_type_record $zone 13 $KSK2 >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $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.autosign
+KSK1=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 7200 $zone 2> keygen.out.$zone.1`
+KSK2=`$KEYGEN -a ECDSAP256SHA256 -f KSK -L 7200 $zone 2> keygen.out.$zone.2`
+ZSK=`$KEYGEN -a ECDSAP256SHA256 -L 7200 $zone 2> keygen.out.$zone.3`
+# Substract DNSKEY TTL from all the times (2h).
+TactN="now-1492h"
+TretN="now-52h"
+TpubN1="now-102h"
+TsbmN1="now-75h"
+TactN1="${TretN}"
+TretN1="now+1388h"
+$SETTIME -s -P $TactN  -A $TactN  -I $TretN  -g $H -k $U $TretN  -r $U $TretN  -d $H $TretN  $KSK1 > settime.out.$zone.1 2>&1
+$SETTIME -s -S $KSK1              -i 0                                                       $KSK2 > settime.out.$zone.3 2>&1
+$SETTIME -s -P $TpubN1 -A $TactN1 -I $TretN1 -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 $KSK2 > settime.out.$zone.1 2>&1
+$SETTIME -s -P $TactN  -A $TactN             -g $O -k $O $TactN  -z $O $TactN                $ZSK  > settime.out.$zone.2 2>&1
+cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" > "$infile"
+private_type_record $zone 13 $KSK1 >> "$infile"
+private_type_record $zone 13 $KSK2 >> "$infile"
+private_type_record $zone 13 $ZSK >> "$infile"
+$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
index f4b06b91ef78a06a0418d7879035b7325545ed74..6bd1d3db9483fc470103e5695b1f93b460659304 100644 (file)
@@ -1080,5 +1080,407 @@ dnssec_verify
 
 # TODO: ED25519 and ED448.
 
+#
+# Zone: expired-sigs.autosign.
+#
+zone_properties "ns3" "expired-sigs.autosign" "autosign" "300" "2"
+# Both KSK and ZSK stay OMNIPRESENT.
+key_properties "KEY1" "ksk" "63072000" "13" "ECDSAP256SHA256" "256" "yes"
+key_timings "KEY1" "published" "active" "retired" "none" "none"
+key_states "KEY1" "omnipresent" "omnipresent" "none" "omnipresent" "omnipresent"
+key_properties "KEY2" "zsk" "31536000" "13" "ECDSAP256SHA256" "256" "yes"
+key_states "KEY2" "omnipresent" "omnipresent" "omnipresent" "none" "none"
+key_timings "KEY2" "published" "active" "retired" "none" "none"
+# Expect only two keys.
+key_clear "KEY3"
+
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Verify all signatures have been refreshed.
+check_rrsig_refresh() {
+       # Apex.
+       _qtypes="DNSKEY SOA NS NSEC"
+       for _qtype in $_qtypes
+       do
+               n=$((n+1))
+               echo_i "check ${_qtype} rrsig is refreshed correctly for zone ${ZONE} ($n)"
+               ret=0
+               dig_with_opts $ZONE @10.53.0.3 $_qtype > dig.out.$DIR.test$n || log_error "dig ${ZONE} ${_qtype} failed"
+               grep "status: NOERROR" dig.out.$DIR.test$n > /dev/null || log_error "mismatch status in DNS response"
+               grep "${ZONE}\..*IN.*RRSIG.*${_qtype}.*${ZONE}" dig.out.$DIR.test$n > rrsig.out.$ZONE.$_qtype || log_error "missing RRSIG (${_qtype}) record in response"
+               # If this exact RRSIG is also in the zone file it is not refreshed.
+               _rrsig=`cat rrsig.out.$ZONE.$_qtype`
+               grep "${_rrsig}" "${DIR}/${ZONE}.db" > /dev/null && log_error "RRSIG (${_qtype}) not refreshed in zone ${ZONE}"
+               test "$ret" -eq 0 || echo_i "failed"
+               status=$((status+ret))
+       done
+
+       # Below apex.
+       _labels="a b c ns3"
+       for _label in $_labels;
+       do
+               _qtypes="A NSEC"
+               for _qtype in $_qtypes
+               do
+                       n=$((n+1))
+                       echo_i "check ${_label} ${_qtype} rrsig is refreshed correctly for zone ${ZONE} ($n)"
+                       ret=0
+                       dig_with_opts "${_label}.${ZONE}" @10.53.0.3 $_qtype > dig.out.$DIR.test$n || log_error "dig ${_label}.${ZONE} ${_qtype} failed"
+                       grep "status: NOERROR" dig.out.$DIR.test$n > /dev/null || log_error "mismatch status in DNS response"
+                       grep "${ZONE}\..*IN.*RRSIG.*${_qtype}.*${ZONE}" dig.out.$DIR.test$n > rrsig.out.$ZONE.$_qtype || log_error "missing RRSIG (${_qtype}) record in response"
+                       _rrsig=`cat rrsig.out.$ZONE.$_qtype`
+                       grep "${_rrsig}" "${DIR}/${ZONE}.db" > /dev/null && log_error "RRSIG (${_qtype}) not refreshed in zone ${ZONE}"
+                       test "$ret" -eq 0 || echo_i "failed"
+                       status=$((status+ret))
+               done
+       done
+}
+
+check_rrsig_refresh
+
+#
+# Zone: fresh-sigs.autosign.
+#
+zone_properties "ns3" "fresh-sigs.autosign" "autosign" "300" "2"
+# key_properties, key_timings and key_states same as above.
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Verify signature reuse.
+check_rrsig_reuse() {
+       # Apex.
+       _qtypes="NS NSEC"
+       for _qtype in $_qtypes
+       do
+               n=$((n+1))
+               echo_i "check ${_qtype} rrsig is reused correctly for zone ${ZONE} ($n)"
+               ret=0
+               dig_with_opts $ZONE @10.53.0.3 $_qtype > dig.out.$DIR.test$n || log_error "dig ${ZONE} ${_qtype} failed"
+               grep "status: NOERROR" dig.out.$DIR.test$n > /dev/null || log_error "mismatch status in DNS response"
+               grep "${ZONE}\..*IN.*RRSIG.*${_qtype}.*${ZONE}" dig.out.$DIR.test$n > rrsig.out.$ZONE.$_qtype || log_error "missing RRSIG (${_qtype}) record in response"
+               # If this exact RRSIG is also in the zone file it is not refreshed.
+               _rrsig=$(awk '{print $5, $6, $7, $8, $9, $10, $11, $12, $13, $14;}' < rrsig.out.$ZONE.$_qtype)
+               grep "${_rrsig}" "${DIR}/${ZONE}.db" > /dev/null || log_error "RRSIG (${_qtype}) not reused in zone ${ZONE}"
+               test "$ret" -eq 0 || echo_i "failed"
+               status=$((status+ret))
+       done
+
+       # Below apex.
+       _labels="a b c ns3"
+       for _label in $_labels;
+       do
+               _qtypes="A NSEC"
+               for _qtype in $_qtypes
+               do
+                       n=$((n+1))
+                       echo_i "check ${_label} ${_qtype} rrsig is reused correctly for zone ${ZONE} ($n)"
+                       ret=0
+                       dig_with_opts "${_label}.${ZONE}" @10.53.0.3 $_qtype > dig.out.$DIR.test$n || log_error "dig ${_label}.${ZONE} ${_qtype} failed"
+                       grep "status: NOERROR" dig.out.$DIR.test$n > /dev/null || log_error "mismatch status in DNS response"
+                       grep "${ZONE}\..*IN.*RRSIG.*${_qtype}.*${ZONE}" dig.out.$DIR.test$n > rrsig.out.$ZONE.$_qtype || log_error "missing RRSIG (${_qtype}) record in response"
+                       _rrsig=$(awk '{print $5, $6, $7, $8, $9, $10, $11, $12, $13, $14;}' < rrsig.out.$ZONE.$_qtype)
+                       grep "${_rrsig}" "${DIR}/${ZONE}.db" > /dev/null || log_error "RRSIG (${_qtype}) not reused in zone ${ZONE}"
+                       test "$ret" -eq 0 || echo_i "failed"
+                       status=$((status+ret))
+               done
+       done
+}
+
+check_rrsig_reuse
+
+#
+# Zone: unfresh-sigs.autosign.
+#
+zone_properties "ns3" "unfresh-sigs.autosign" "autosign" "300" "2"
+# key_properties, key_timings and key_states same as above.
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+check_rrsig_refresh
+
+#
+# Zone: zsk-missing.autosign.
+#
+zone_properties "ns3" "zsk-missing.autosign" "autosign" "300" "2"
+# KSK stays OMNIPRESENT.
+key_properties "KEY1" "ksk" "63072000" "13" "ECDSAP256SHA256" "256" "yes"
+key_timings "KEY1" "published" "active" "retired" "none" "none"
+key_states "KEY1" "omnipresent" "omnipresent" "none" "omnipresent" "omnipresent"
+# key_properties, key_timings and key_states same as above.
+# TODO
+
+#
+# Zone: zsk-retired.autosign.
+#
+zone_properties "ns3" "zsk-retired.autosign" "autosign" "300" "3"
+# KSK properties, timings and states same as above.
+# The ZSK goal is set to HIDDEN but records stay OMNIPRESENT until the new ZSK
+# is active.
+key_properties "KEY2" "zsk" "31536000" "13" "ECDSAP256SHA256" "256" "yes"
+key_timings "KEY2" "published" "active" "retired" "none" "none"
+key_states "KEY2" "hidden" "omnipresent" "omnipresent" "none" "none"
+# A new ZSK should be introduced, so expect a key with goal OMNIPRESENT,
+# the DNSKEY introduced (RUMOURED) and the signatures HIDDEN.
+key_properties "KEY3" "zsk" "31536000" "13" "ECDSAP256SHA256" "256" "no"
+key_timings "KEY3" "published" "active" "retired" "none" "none"
+key_states "KEY3" "omnipresent" "rumoured" "hidden" "none" "none"
+
+#
+# Testing ZSK Pre-Publication rollover.
+#
+
+#
+# Zone: step1.zsk-prepub.autosign.
+#
+zone_properties "ns3" "step1.zsk-prepub.autosign" "zsk-prepub" "3600" "2"
+# Both KSK (KEY1) and ZSK (KEY2) start in OMNIPRESENT.
+key_properties "KEY1" "ksk" "63072000" "13" "ECDSAP256SHA256" "256" "yes"
+key_timings "KEY1" "published" "active" "retired" "none" "none"
+key_states "KEY1" "omnipresent" "omnipresent" "none" "omnipresent" "omnipresent"
+key_properties "KEY2" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "yes"
+key_states "KEY2" "omnipresent" "omnipresent" "omnipresent" "none" "none"
+key_timings "KEY2" "published" "active" "retired" "none" "none"
+# Initially only two keys.
+key_clear "KEY3"
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+check_next_key_event() {
+       _expect=$1
+
+       n=$((n+1))
+       echo_i "check next key event for zone ${ZONE} ($n)"
+       ret=0
+       grep "zone ${ZONE}.*: next key event in .* seconds" "${DIR}/named.run" > keyevent.out.$ZONE.test$n || log_error "no next key event for zone ${ZONE}"
+
+       _time=$(awk '{print $10}' < keyevent.out.$ZONE.test$n)
+
+       # The next key event time must within 10 seconds of the
+       # expected time.
+       _expectmin=$((_expect-10))
+       _expectmax=$((_expect+10))
+
+       test $_expectmin -le $_time || log_error "bad next key event time ${_time} for zone ${ZONE} (expect ${_expect})"
+       test $_expectmax -ge $_time || log_error "bad next key event time ${_time} for zone ${ZONE} (expect ${_expect})"
+
+       test "$ret" -eq 0 || echo_i "failed"
+       status=$((status+ret))
+}
+
+# Next key event is when the successor ZSK needs to be published.  That is
+# the ZSK lifetime - prepublication time.  The prepublication time is DNSKEY
+# TTL plus publish safety plus the zone propagation delay.  For the
+# zsk-prepub policy that means: 30d - 3600s + 1d + 1h = 2498400 seconds.
+check_next_key_event 2498400
+
+#
+# Zone: step2.zsk-prepub.autosign.
+#
+zone_properties "ns3" "step2.zsk-prepub.autosign" "zsk-prepub" "3600" "3"
+# KSK (KEY1) doesn't change.
+# ZSK (KEY2) remains active, no change in properties/timings/states.
+# New ZSK (KEY3) is prepublished.
+key_properties "KEY3" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "no"
+key_states "KEY3" "omnipresent" "rumoured" "hidden" "none" "none"
+key_timings "KEY3" "published" "active" "retired" "none" "none"
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Next key event is when the successor ZSK becomes OMNIPRESENT.  That is the
+# DNSKEY TTL plus the zone propagation delay, plus the publish-safety. For
+# the zsk-prepub policy, this means: 3600s + 1h + 1d = 93600 seconds.
+check_next_key_event 93600
+
+#
+# Zone: step3.zsk-prepub.autosign.
+#
+zone_properties "ns3" "step3.zsk-prepub.autosign" "zsk-prepub" "3600" "3"
+# KSK (KEY1) doesn't change.
+# ZSK (KEY2) properties and timing metadata same as above.
+# ZSK (KEY2) no longer is actively signing, RRSIG state in UNRETENTIVE.
+# New ZSK (KEY3) is now actively signing, RRSIG state in RUMOURED.
+key_properties "KEY2" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "no"
+key_states "KEY2" "hidden" "omnipresent" "unretentive" "none" "none"
+
+key_properties "KEY3" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "yes"
+key_states "KEY3" "omnipresent" "omnipresent" "rumoured" "none" "none"
+check_keys
+check_apex
+# Subdomain still has good signatures of ZSK (KEY2)
+key_properties "KEY2" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "yes"
+key_properties "KEY3" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "no"
+check_subdomain
+dnssec_verify
+
+# Next key event is when all the RRSIG records have been replaced with
+# signatures of the new ZSK, in other words when ZRRSIG becomes OMNIPRESENT.
+# That is Dsgn plus the maximum zone TTL plus the zone propagation delay plus
+# retire-safety. For the zsk-prepub policy that means: 1w (because 2w validity
+# and refresh within a week) + 1d + 1h + 2d = 10d1h = 867600 seconds.
+check_next_key_event 867600
+
+#
+# Zone: step4.zsk-prepub.autosign.
+#
+zone_properties "ns3" "step4.zsk-prepub.autosign" "zsk-prepub" "3600" "3"
+# KSK (KEY1) doesn't change.
+# ZSK (KEY2) properties and timing metadata same as above.
+# ZSK (KEY2) DNSKEY is no longer needed.
+# ZSK (KEY3) is now actively signing, RRSIG state in RUMOURED.
+key_properties "KEY2" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "no"
+key_states "KEY2" "hidden" "unretentive" "hidden" "none" "none"
+key_properties "KEY3" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "yes"
+key_states "KEY3" "omnipresent" "omnipresent" "omnipresent" "none" "none"
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Next key event is when the DNSKEY enters the HIDDEN state.  This is the
+# DNSKEY TTL plus zone propagation delay. For the zsk-prepub policy this is:
+# 3600s + 1h = 7200s
+check_next_key_event 7200
+
+#
+# Zone: step5.zsk-prepub.autosign.
+#
+zone_properties "ns3" "step5.zsk-prepub.autosign" "zsk-prepub" "3600" "3"
+# KSK (KEY1) doesn't change.
+# ZSK (KEY2) properties and timing metadata same as above.
+# ZSK (KEY3) DNSKEY is now completely HIDDEN and removed.
+key_timings "KEY2" "published" "active" "retired" "none" "removed"
+key_states "KEY2" "hidden" "hidden" "hidden" "none" "none"
+# ZSK (KEY3) remains actively signing, staying in OMNIPRESENT.
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Next key event is when the new successor needs to be published.  This is the
+# ZSK lifetime minus Iret minus Ipub minus DNSKEY TTL.  For the zsk-prepub
+# policy this is: 30d - 867600s - 93600s - 3600s = 1627200 seconds.
+check_next_key_event 1627200
+
+#
+# Testing KSK Double-KSK rollover.
+#
+
+#
+# Zone: step1.ksk-doubleksk.autosign.
+#
+zone_properties "ns3" "step1.ksk-doubleksk.autosign" "ksk-doubleksk" "7200" "2"
+# Both KSK (KEY1) and ZSK (KEY2) start in OMNIPRESENT.
+key_properties "KEY1" "ksk" "5184000" "13" "ECDSAP256SHA256" "256" "yes"
+key_timings "KEY1" "published" "active" "retired" "none" "none"
+key_states "KEY1" "omnipresent" "omnipresent" "none" "omnipresent" "omnipresent"
+key_properties "KEY2" "zsk" "31536000" "13" "ECDSAP256SHA256" "256" "yes"
+key_timings "KEY2" "published" "active" "retired" "none" "none"
+key_states "KEY2" "omnipresent" "omnipresent" "omnipresent" "none" "none"
+# Initially only two keys.
+key_clear "KEY3"
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Next key event is when the successor KSK needs to be published.  That is
+# the KSK lifetime - prepublication time - DS registration delay.  The
+# prepublication time is DNSKEY TTL plus publish safety plus the zone
+# propagation delay.  For the ksk-doubleksk policy that means:
+# 60d - (1d3h) - (1d) = 5000400 seconds.
+check_next_key_event 5000400
+
+#
+# Zone: step2.ksk-doubleksk.autosign.
+#
+zone_properties "ns3" "step2.ksk-doubleksk.autosign" "ksk-doubleksk" "7200" "3"
+# ZSK (KEY2) doesn't change.
+# KSK (KEY1) remains active, no change in properties/timings/states.
+# New KSK (KEY3) is prepublished (and signs DNSKEY RRset).
+key_properties "KEY3" "ksk" "5184000" "13" "ECDSAP256SHA256" "256" "yes"
+key_states "KEY3" "omnipresent" "rumoured" "none" "rumoured" "hidden"
+key_timings "KEY3" "published" "active" "retired" "none" "none"
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Next key event is when the successor KSK becomes OMNIPRESENT.  That is the
+# DNSKEY TTL plus the zone propagation delay, plus the publish-safety.  For
+# the ksk-doubleksk policy, this means: 7200s + 1h + 1d = 97200 seconds.
+check_next_key_event 97200
+
+#
+# Zone: step3.ksk-doubleksk.autosign.
+#
+zone_properties "ns3" "step3.ksk-doubleksk.autosign" "ksk-doubleksk" "7200" "3"
+# ZSK (KEY2) doesn't change.
+# KSK (KEY1) DS will be removed, so it is UNRETENTIVE.
+key_states "KEY1" "hidden" "omnipresent" "none" "omnipresent" "unretentive"
+# New KSK (KEY3) has its DS submitted.
+key_states "KEY3" "omnipresent" "omnipresent" "none" "omnipresent" "rumoured"
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Next key event is when the predecessor DS has been replaced with the
+# successor DS and enough time has passed such that the all validators that
+# have this DS RRset cached only know about the successor DS.  This is the
+# registration delay plus the retire interval, which is the parent
+# propagation delay plus the DS TTL plus the retire-safety.  For the
+# ksk-double-ksk policy this means: 1d + 1h + 3600s + 2d = 3d2h =
+# 266400 seconds.
+check_next_key_event 266400
+
+#
+# Zone: step4.ksk-doubleksk.autosign.
+#
+zone_properties "ns3" "step4.ksk-doubleksk.autosign" "ksk-doubleksk" "7200" "3"
+# ZSK (KEY2) doesn't change.
+# KSK (KEY1) DNSKEY can be removed.
+key_properties "KEY1" "ksk" "5184000" "13" "ECDSAP256SHA256" "256" "no"
+key_states "KEY1" "hidden" "unretentive" "none" "unretentive" "hidden"
+# New KSK (KEY3) DS is now OMNIPRESENT.
+key_states "KEY3" "omnipresent" "omnipresent" "none" "omnipresent" "omnipresent"
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Next key event is when the DNSKEY enters the HIDDEN state.  This is the
+# DNSKEY TTL plus zone propagation delay. For the ksk-doubleksk policy this is:
+# 7200s + 1h = 10800s
+check_next_key_event 10800
+
+#
+# Zone: step5.ksk-doubleksk.autosign.
+#
+zone_properties "ns3" "step5.ksk-doubleksk.autosign" "ksk-doubleksk" "7200" "3"
+# ZSK (KEY2) doesn't change.
+# KSK (KEY1) DNSKEY is now HIDDEN.
+key_states "KEY1" "hidden" "hidden" "none" "hidden" "hidden"
+# New KSK (KEY3) stays OMNIPRESENT.
+check_keys
+check_apex
+check_subdomain
+dnssec_verify
+
+# Next key event is when the new successor needs to be published.  This is the
+# KSK lifetime minus Ipub minus Dreg minus Iret minus DNSKEY TTL.  For the
+# ksk-doubleksk this is: 60d - 1d3h - 1d - 2d2h - 2h =
+# 5184000 - 97200 - 86400 - 180000 - 7200 = 4813200 seconds.
+check_next_key_event 4813200
+
 echo_i "exit status: $status"
 [ $status -eq 0 ] || exit 1