]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
dnssec-ksr keygen -o to create KSKs
authorMatthijs Mekking <matthijs@isc.org>
Tue, 3 Sep 2024 15:24:22 +0000 (17:24 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Fri, 1 Nov 2024 14:50:16 +0000 (15:50 +0100)
Add an option to dnssec-ksr keygen, -o, to create KSKs instead of ZSKs.
This way, we can create a set of KSKS for a given period too.

For KSKs we also need to set timing metadata, including "SyncPublish"
and "SyncDelete". This functionality already exists in keymgr.c so
let's make the function accessible.

Replace dnssec-keygen calls with dnssec-ksr keygen for KSK in the
ksr system test and check keys for created KSKs as well. This requires
a slight modification of the check_keys function to take into account
KSK timings and metadata.

bin/dnssec/dnssec-ksr.c
bin/dnssec/dnssec-ksr.rst
bin/tests/system/ksr/tests_ksr.py
lib/dns/include/dns/keymgr.h
lib/dns/keymgr.c

index 9919fd42105f38473bf37182f0aecba49d5a982d..633e2f7e50df1dd861cc8ff376506d7e175dd6e3 100644 (file)
@@ -25,6 +25,7 @@
 #include <dns/callbacks.h>
 #include <dns/dnssec.h>
 #include <dns/fixedname.h>
+#include <dns/keymgr.h>
 #include <dns/keyvalues.h>
 #include <dns/rdataclass.h>
 #include <dns/rdatalist.h>
@@ -61,16 +62,19 @@ struct ksr_ctx {
        bool setstart;
        bool setend;
        /* keygen */
+       bool ksk;
        dns_ttl_t ttl;
        dns_secalg_t alg;
        int size;
        time_t lifetime;
+       time_t parentpropagation;
        time_t propagation;
        time_t publishsafety;
        time_t retiresafety;
        time_t sigrefresh;
        time_t sigvalidity;
        time_t signdelay;
+       time_t ttlds;
        time_t ttlsig;
 };
 typedef struct ksr_ctx ksr_ctx_t;
@@ -239,6 +243,7 @@ get_dnskeys(ksr_ctx_t *ksr, dns_dnsseckeylist_t *keys) {
 
 static void
 setcontext(ksr_ctx_t *ksr, dns_kasp_t *kasp) {
+       ksr->parentpropagation = dns_kasp_parentpropagationdelay(kasp);
        ksr->propagation = dns_kasp_zonepropagationdelay(kasp);
        ksr->publishsafety = dns_kasp_publishsafety(kasp);
        ksr->retiresafety = dns_kasp_retiresafety(kasp);
@@ -246,6 +251,7 @@ setcontext(ksr_ctx_t *ksr, dns_kasp_t *kasp) {
        ksr->sigrefresh = dns_kasp_sigrefresh(kasp);
        ksr->signdelay = dns_kasp_signdelay(kasp);
        ksr->ttl = dns_kasp_dnskeyttl(kasp);
+       ksr->ttlds = dns_kasp_dsttl(kasp);
        ksr->ttlsig = dns_kasp_zonemaxttl(kasp, true);
 }
 
@@ -313,12 +319,13 @@ freerrset(dns_rdataset_t *rdataset) {
 }
 
 static void
-create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys,
-          isc_stdtime_t inception, isc_stdtime_t active,
-          isc_stdtime_t *expiration) {
+create_key(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_kasp_key_t *kaspkey,
+          dns_dnsseckeylist_t *keys, isc_stdtime_t inception,
+          isc_stdtime_t active, isc_stdtime_t *expiration) {
        bool conflict = false;
        bool freekey = false;
        bool show_progress = true;
+       bool first = true;
        char algstr[DNS_SECALG_FORMATSIZE];
        char filename[PATH_MAX + 1];
        char timestr[26]; /* Minimal buf as per ctime_r() spec. */
@@ -327,9 +334,15 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys,
        isc_buffer_t buf;
        isc_result_t ret;
        isc_stdtime_t prepub;
+       uint16_t flags = DNS_KEYOWNER_ZONE;
 
        isc_stdtime_tostring(inception, timestr, sizeof(timestr));
 
+       /* ZSK or KSK? */
+       if (ksr->ksk) {
+               flags |= DNS_KEYFLAG_KSK;
+       }
+
        /* Check algorithm and size. */
        dns_secalg_format(ksr->alg, algstr, sizeof(algstr));
        if (!dst_algorithm_supported(ksr->alg)) {
@@ -403,6 +416,7 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys,
                                "Selecting key pair for bundle %s: ", timestr);
                        fflush(stderr);
                }
+               first = false;
                key = dk->key;
                *expiration = inact;
                goto output;
@@ -420,18 +434,18 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys,
                        ret = dns_keystore_keygen(
                                ksr->keystore, name, ksr->policy,
                                dns_rdataclass_in, mctx, ksr->alg, ksr->size,
-                               DNS_KEYOWNER_ZONE, &key);
+                               flags, &key);
                } else if (show_progress) {
-                       ret = dst_key_generate(
-                               name, ksr->alg, ksr->size, 0, DNS_KEYOWNER_ZONE,
-                               DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, NULL,
-                               mctx, &key, &progress);
+                       ret = dst_key_generate(name, ksr->alg, ksr->size, 0,
+                                              flags, DNS_KEYPROTO_DNSSEC,
+                                              dns_rdataclass_in, NULL, mctx,
+                                              &key, &progress);
                        fflush(stderr);
                } else {
-                       ret = dst_key_generate(
-                               name, ksr->alg, ksr->size, 0, DNS_KEYOWNER_ZONE,
-                               DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, NULL,
-                               mctx, &key, NULL);
+                       ret = dst_key_generate(name, ksr->alg, ksr->size, 0,
+                                              flags, DNS_KEYPROTO_DNSSEC,
+                                              dns_rdataclass_in, NULL, mctx,
+                                              &key, NULL);
                }
 
                if (ret != ISC_R_SUCCESS) {
@@ -468,15 +482,27 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys,
        prepub = ksr->ttl + ksr->publishsafety + ksr->propagation;
        dst_key_setttl(key, ksr->ttl);
        dst_key_setnum(key, DST_NUM_LIFETIME, ksr->lifetime);
-       dst_key_setbool(key, DST_BOOL_KSK, false);
-       dst_key_setbool(key, DST_BOOL_ZSK, true);
+       dst_key_setbool(key, DST_BOOL_KSK, ksr->ksk);
+       dst_key_setbool(key, DST_BOOL_ZSK, !ksr->ksk);
        dst_key_settime(key, DST_TIME_CREATED, ksr->now);
        dst_key_settime(key, DST_TIME_PUBLISH, (active - prepub));
        dst_key_settime(key, DST_TIME_ACTIVATE, active);
+       if (ksr->ksk) {
+               dns_keymgr_settime_syncpublish(key, kasp, first);
+       }
+
        if (ksr->lifetime > 0) {
                isc_stdtime_t inactive = (active + ksr->lifetime);
-               isc_stdtime_t remove = ksr->ttlsig + ksr->propagation +
-                                      ksr->retiresafety + ksr->signdelay;
+               isc_stdtime_t remove;
+
+               if (ksr->ksk) {
+                       remove = ksr->ttlds + ksr->parentpropagation +
+                                ksr->retiresafety;
+                       dst_key_settime(key, DST_TIME_SYNCDELETE, inactive);
+               } else {
+                       remove = ksr->ttlsig + ksr->propagation +
+                                ksr->retiresafety + ksr->signdelay;
+               }
                dst_key_settime(key, DST_TIME_INACTIVE, inactive);
                dst_key_settime(key, DST_TIME_DELETE, (inactive + remove));
                *expiration = inactive;
@@ -492,6 +518,8 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys,
                      isc_result_totext(ret));
        }
 
+       first = false;
+
 output:
        isc_buffer_clear(&buf);
        ret = dst_key_buildfilename(key, 0, NULL, &buf);
@@ -907,9 +935,12 @@ keygen(ksr_ctx_t *ksr) {
        for (dns_kasp_key_t *kk = ISC_LIST_HEAD(dns_kasp_keys(kasp));
             kk != NULL; kk = ISC_LIST_NEXT(kk, link))
        {
-               if (dns_kasp_key_ksk(kk)) {
+               if (dns_kasp_key_ksk(kk) && !ksr->ksk) {
                        /* only ZSKs allowed */
                        continue;
+               } else if (dns_kasp_key_zsk(kk) && ksr->ksk) {
+                       /* only KSKs allowed */
+                       continue;
                }
                ksr->alg = dns_kasp_key_algorithm(kk);
                ksr->lifetime = dns_kasp_key_lifetime(kk);
@@ -920,7 +951,7 @@ keygen(ksr_ctx_t *ksr) {
                for (isc_stdtime_t inception = ksr->start, act = ksr->start;
                     inception < ksr->end; inception += ksr->lifetime)
                {
-                       create_zsk(ksr, kk, &keys, inception, act, &act);
+                       create_key(ksr, kasp, kk, &keys, inception, act, &act);
                        if (ksr->lifetime == 0) {
                                /* unlimited lifetime, but not infinite loop */
                                break;
@@ -928,7 +959,7 @@ keygen(ksr_ctx_t *ksr) {
                }
        }
        if (noop) {
-               fatal("policy '%s' has no zsks", ksr->policy);
+               fatal("no keys created for policy '%s'", ksr->policy);
        }
        /* Cleanup */
        cleanup(&keys, kasp);
@@ -1212,7 +1243,7 @@ main(int argc, char *argv[]) {
 
        isc_commandline_errprint = false;
 
-#define OPTIONS "E:e:Ff:hi:K:k:l:v:V"
+#define OPTIONS "E:e:Ff:hi:K:k:l:ov:V"
        while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) {
                switch (ch) {
                case 'E':
@@ -1249,6 +1280,9 @@ main(int argc, char *argv[]) {
                case 'l':
                        ksr.configfile = isc_commandline_argument;
                        break;
+               case 'o':
+                       ksr.ksk = true;
+                       break;
                case 'V':
                        version(program);
                        break;
index e4ae2ce887a79882c16262b168638fa73b762112..b8d6c43d95af3e516b2509bb7e83562bd597253f 100644 (file)
@@ -21,7 +21,7 @@ dnssec-ksr - Create signed key response (SKR) files for offline KSK setups
 Synopsis
 ~~~~~~~~
 
-:program:`dnssec-ksr` [**-e** date/offset] [**-F**] [**-f** file] [**-h**] [**-i** date/offset] [**-K** directory] [**-k** policy] [**-l** file] [**-V**] [**-v** level] {command} {zone}
+:program:`dnssec-ksr` [**-e** date/offset] [**-F**] [**-f** file] [**-h**] [**-i** date/offset] [**-K** directory] [**-k** policy] [**-l** file] [**-o**] [**-V**] [**-v** level] {command} {zone}
 
 Description
 ~~~~~~~~~~~
@@ -80,6 +80,11 @@ Options
    This option provides a configuration file that contains a ``dnssec-policy``
    statement (matching the policy set with :option:`-k`).
 
+.. option:: -o
+
+   Normally when pregenerating keys, ZSKs are created. When this option is
+   set, create KSKs instead.
+
 .. option:: -V
 
    This option prints version information.
@@ -102,9 +107,8 @@ Commands
 
 .. option:: keygen
 
-  Pregenerate a number of zone signing keys (ZSKs), given a DNSSEC policy and
-  an interval. The number of generated keys depends on the interval and the
-  ZSK lifetime.
+  Pregenerate a number of keys, given a DNSSEC policy and an interval. The
+  number of generated keys depends on the interval and the key lifetime.
 
 .. option:: request
 
@@ -127,7 +131,7 @@ occurred.
 Examples
 ~~~~~~~~
 
-When you need to generate keys for the zone "example.com" for the next year,
+When you need to generate ZSKs for the zone "example.com" for the next year,
 given a ``dnssec-policy`` named "mypolicy":
 
 ::
@@ -140,7 +144,8 @@ Creating a KSR for the same zone and period can be done with:
 
     dnssec-ksr -i now -e +1y -k mypolicy -l named.conf request example.com > ksr.txt
 
-Typically you would now transfer the KSR to the system that has access to the KSK.
+Typically you would now transfer the KSR to the system that has access to
+the KSK.
 
 Signing the KSR created above can be done with:
 
@@ -148,7 +153,8 @@ Signing the KSR created above can be done with:
 
     dnssec-ksr -i now -e +1y -k kskpolicy -l named.conf -f ksr.txt sign example.com
 
-Make sure that the DNSSEC parameters in ``kskpolicy`` match those in ``mypolicy``.
+Make sure that the DNSSEC parameters in ``kskpolicy`` match those
+in ``mypolicy``.
 
 See Also
 ~~~~~~~~
index 793df81c4cb7912b2aadce6bcddeac54574a55d5..3790003f33dca4666be93ad90e88ca1659af1636 100644 (file)
@@ -55,28 +55,6 @@ def keystr_to_keylist(keystr: str, keydir: Optional[str] = None) -> List[Key]:
     return [Key(name, keydir) for name in keystr.split()]
 
 
-def keygen(zone, policy, keydir, when="now"):
-    keygen_command = [
-        os.environ.get("KEYGEN"),
-        "-l",
-        "ns1/named.conf",
-        "-fK",
-        "-K",
-        keydir,
-        "-k",
-        policy,
-        "-P",
-        when,
-        "-A",
-        when,
-        "-P",
-        "sync",
-        when,
-        zone,
-    ]
-    return isctest.run.cmd(keygen_command, log_stdout=True).stdout.decode("utf-8")
-
-
 def ksr(zone, policy, action, options="", raise_on_exception=True):
     ksr_command = [
         os.environ.get("KSR"),
@@ -123,12 +101,22 @@ def check_keys(
         # retired: zsk-lifetime
         if lifetime is not None:
             retired = active + lifetime
-            # removed: ttlsig + retire-safety + sign-delay + propagation
-            removed = retired + timedelta(days=10, hours=1, minutes=5)
+
+            if key.is_ksk():
+                # removed: ttlds + retire-safety + parent-propagation
+                removed = retired + timedelta(days=1, hours=2)
+            else:
+                # removed: ttlsig + retire-safety + sign-delay + propagation
+                removed = retired + timedelta(days=10, hours=1, minutes=5)
         else:
             retired = None
             removed = None
 
+        goal = "hidden"
+        state_dnskey = "hidden"
+        state_zrrsig = "hidden"
+        state_krrsig = "hidden"
+        state_ds = "hidden"
         if retired is None or between(now, published, retired):
             goal = "omnipresent"
             pubdelay = published + timedelta(hours=2, minutes=5)
@@ -136,24 +124,34 @@ def check_keys(
 
             if between(now, published, pubdelay):
                 state_dnskey = "rumoured"
+                state_krrsig = "rumoured"
             else:
                 state_dnskey = "omnipresent"
+                state_krrsig = "omnipresent"
 
-            if between(now, active, signdelay):
-                state_zrrsig = "rumoured"
+            if key.is_ksk():
+                state_ds = "hidden"
             else:
-                state_zrrsig = "omnipresent"
-        else:
-            goal = "hidden"
-            state_dnskey = "hidden"
-            state_zrrsig = "hidden"
+                if between(now, active, signdelay):
+                    state_zrrsig = "rumoured"
+                else:
+                    state_zrrsig = "omnipresent"
 
         with open(key.statefile, "r", encoding="utf-8") as file:
             metadata = file.read()
             assert f"Algorithm: {alg}" in metadata
             assert f"Length: {size}" in metadata
-            assert "KSK: no" in metadata
-            assert "ZSK: yes" in metadata
+
+            if key.is_ksk():
+                assert "KSK: yes" in metadata
+            else:
+                assert "KSK: no" in metadata
+
+            if key.is_zsk():
+                assert "ZSK: yes" in metadata
+            else:
+                assert "ZSK: no" in metadata
+
             assert f"Published: {published}" in metadata
             assert f"Active: {active}" in metadata
 
@@ -169,9 +167,18 @@ def check_keys(
             if with_state:
                 assert f"GoalState: {goal}" in metadata
                 assert f"DNSKEYState: {state_dnskey}" in metadata
-                assert f"ZRRSIGState: {state_zrrsig}" in metadata
-                assert "KRRSIGState:" not in metadata
-                assert "DSState:" not in metadata
+
+                if key.is_ksk():
+                    assert f"KRRSIGState: {state_krrsig}" in metadata
+                    assert f"DSState: {state_ds}" in metadata
+                else:
+                    assert "KRRSIGState:" not in metadata
+                    assert "DSState:" not in metadata
+
+                if key.is_zsk():
+                    assert f"ZRRSIGState: {state_zrrsig}" in metadata
+                else:
+                    assert "ZRRSIGState:" not in metadata
 
         num += 1
 
@@ -264,6 +271,9 @@ def check_signedkeyresponse(
         # expect ksks
         for key in sorted(ksks):
             published = key.get_timing("Publish")
+            if between(published, inception, next_bundle):
+                next_bundle = published
+
             removed = key.get_timing("Delete", must_exist=False)
 
             if published > inception:
@@ -271,6 +281,9 @@ def check_signedkeyresponse(
             if removed is not None and inception >= removed:
                 continue
 
+            if between(removed, inception, next_bundle):
+                next_bundle = removed
+
             # this ksk must be in the ksr
             assert key.dnskey_equals(lines[line_no])
             line_no += 1
@@ -399,7 +412,7 @@ def test_ksr_errors():
     _, err = ksr(
         "csk.test", "csk", "keygen", options="-K ns1 -e +2y", raise_on_exception=False
     )
-    assert "dnssec-ksr: fatal: policy 'csk' has no zsks" in err
+    assert "dnssec-ksr: fatal: no keys created for policy 'csk'" in err
 
     # check that 'dnssec-ksr request' errors on missing end date
     _, err = ksr("common.test", "common", "request", raise_on_exception=False)
@@ -424,10 +437,12 @@ def test_ksr_common(servers):
 
     # create ksk
     kskdir = "ns1/offline"
-    out = keygen(zone, policy, kskdir)
+    out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i now -e +1y -o")
     ksks = keystr_to_keylist(out, kskdir)
     assert len(ksks) == 1
 
+    check_keys(ksks, None)
+
     # check that 'dnssec-ksr keygen' pregenerates right amount of keys
     out, _ = ksr(zone, policy, "keygen", options="-i now -e +1y")
     zsks = keystr_to_keylist(out)
@@ -611,13 +626,13 @@ def test_ksr_lastbundle(servers):
 
     # create ksk
     kskdir = "ns1/offline"
-    now = KeyTimingMetadata.now()
     offset = -timedelta(days=365)
-    when = now + offset - timedelta(days=1)
-    out = keygen(zone, policy, kskdir, when=str(when))
+    out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i -1y -e +1d -o")
     ksks = keystr_to_keylist(out, kskdir)
     assert len(ksks) == 1
 
+    check_keys(ksks, None, offset=offset)
+
     # check that 'dnssec-ksr keygen' pregenerates right amount of keys
     zskdir = "ns1"
     out, _ = ksr(zone, policy, "keygen", options=f"-K {zskdir} -i -1y -e +1d")
@@ -690,13 +705,13 @@ def test_ksr_inthemiddle(servers):
 
     # create ksk
     kskdir = "ns1/offline"
-    now = KeyTimingMetadata.now()
     offset = -timedelta(days=365)
-    when = now + offset - timedelta(days=1)
-    out = keygen(zone, policy, kskdir, when=str(when))
+    out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i -1y -e +1y -o")
     ksks = keystr_to_keylist(out, kskdir)
     assert len(ksks) == 1
 
+    check_keys(ksks, None, offset=offset)
+
     # check that 'dnssec-ksr keygen' pregenerates right amount of keys
     zskdir = "ns1"
     out, _ = ksr(zone, policy, "keygen", options=f"-K {zskdir} -i -1y -e +1y")
@@ -771,7 +786,7 @@ def check_ksr_rekey_logs_error(server, zone, policy, offset, end):
     now = KeyTimingMetadata.now()
     then = now + offset
     until = now + end
-    out = keygen(zone, policy, kskdir, when=str(then))
+    out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i {then} -e {until} -o")
     ksks = keystr_to_keylist(out, kskdir)
     assert len(ksks) == 1
 
@@ -844,10 +859,12 @@ def test_ksr_unlimited(servers):
 
     # create ksk
     kskdir = "ns1/offline"
-    out = keygen(zone, policy, kskdir)
+    out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i now -e +2y -o")
     ksks = keystr_to_keylist(out, kskdir)
     assert len(ksks) == 1
 
+    check_keys(ksks, None)
+
     # check that 'dnssec-ksr keygen' pregenerates right amount of keys
     zskdir = "ns1"
     out, _ = ksr(zone, policy, "keygen", options=f"-K {zskdir} -i now -e +2y")
@@ -959,10 +976,28 @@ def test_ksr_twotone(servers):
 
     # create ksk
     kskdir = "ns1/offline"
-    out = keygen(zone, policy, kskdir)
+    out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i now -e +1y -o")
     ksks = keystr_to_keylist(out, kskdir)
     assert len(ksks) == 2
 
+    ksks_defalg = []
+    ksks_altalg = []
+    for ksk in ksks:
+        alg = ksk.get_metadata("Algorithm")
+        if alg == os.environ.get("DEFAULT_ALGORITHM_NUMBER"):
+            ksks_defalg.append(ksk)
+        elif alg == os.environ.get("ALTERNATIVE_ALGORITHM_NUMBER"):
+            ksks_altalg.append(ksk)
+
+    assert len(ksks_defalg) == 1
+    assert len(ksks_altalg) == 1
+
+    check_keys(ksks_defalg, None)
+
+    alg = os.environ.get("ALTERNATIVE_ALGORITHM_NUMBER")
+    size = os.environ.get("ALTERNATIVE_BITS")
+    check_keys(ksks_altalg, None, alg, size)
+
     # check that 'dnssec-ksr keygen' pregenerates right amount of keys
     zskdir = "ns1"
     out, _ = ksr(zone, policy, "keygen", options=f"-K {zskdir} -i now -e +1y")
index 61cf3df7aa712a06b06eb87a75b0c7c161d66f03..deb007623fbfb12e02ac7a0bc4dd7fd80d1205cc 100644 (file)
 
 ISC_LANG_BEGINDECLS
 
+void
+dns_keymgr_settime_syncpublish(dst_key_t *key, dns_kasp_t *kasp, bool first);
+/*%<
+ * Set the SyncPublish time (when the DS may be submitted to the parent).
+ * If 'first' is true, also make sure that the zone signatures are omnipresent.
+ *
+ *      Requires:
+ *\li           'key' is a valid DNSSEC key.
+ *\li           'kasp' is a valid DNSSEC policy.
+ */
+
 isc_result_t
 dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
               isc_mem_t *mctx, dns_dnsseckeylist_t *keyring,
index f1cc28ed25424fbf531c0c8bd45c706091854ef3..1f5ab70c9b1f18bdafd0bffc38f9c0848aba0c5f 100644 (file)
@@ -163,26 +163,25 @@ keymgr_settime_remove(dns_dnsseckey_t *key, dns_kasp_t *kasp) {
  * Set the SyncPublish time (when the DS may be submitted to the parent).
  *
  */
-static void
-keymgr_settime_syncpublish(dns_dnsseckey_t *key, dns_kasp_t *kasp, bool first) {
+void
+dns_keymgr_settime_syncpublish(dst_key_t *key, dns_kasp_t *kasp, bool first) {
        isc_stdtime_t published, syncpublish;
        bool ksk = false;
        isc_result_t ret;
 
        REQUIRE(key != NULL);
-       REQUIRE(key->key != NULL);
 
-       ret = dst_key_gettime(key->key, DST_TIME_PUBLISH, &published);
+       ret = dst_key_gettime(key, DST_TIME_PUBLISH, &published);
        if (ret != ISC_R_SUCCESS) {
                return;
        }
 
-       ret = dst_key_getbool(key->key, DST_BOOL_KSK, &ksk);
+       ret = dst_key_getbool(key, DST_BOOL_KSK, &ksk);
        if (ret != ISC_R_SUCCESS || !ksk) {
                return;
        }
 
-       syncpublish = published + dst_key_getttl(key->key) +
+       syncpublish = published + dst_key_getttl(key) +
                      dns_kasp_zonepropagationdelay(kasp) +
                      dns_kasp_publishsafety(kasp);
        if (first) {
@@ -196,7 +195,7 @@ keymgr_settime_syncpublish(dns_dnsseckey_t *key, dns_kasp_t *kasp, bool first) {
                        syncpublish = zrrsig_present;
                }
        }
-       dst_key_settime(key->key, DST_TIME_SYNCPUBLISH, syncpublish);
+       dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncpublish);
 }
 
 /*
@@ -1862,7 +1861,7 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key,
                 */
                dst_key_settime(new_key->key, DST_TIME_PUBLISH, now);
                dst_key_settime(new_key->key, DST_TIME_ACTIVATE, now);
-               keymgr_settime_syncpublish(new_key, kasp, true);
+               dns_keymgr_settime_syncpublish(new_key->key, kasp, true);
                active = now;
        } else {
                /*
@@ -1894,7 +1893,7 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key,
                }
                dst_key_settime(new_key->key, DST_TIME_PUBLISH, prepub);
                dst_key_settime(new_key->key, DST_TIME_ACTIVATE, active);
-               keymgr_settime_syncpublish(new_key, kasp, false);
+               dns_keymgr_settime_syncpublish(new_key->key, kasp, false);
 
                /*
                 * Retire predecessor.