Index: enforcer-ng/src/policy/kasp.proto
===================================================================
--- enforcer-ng/src/policy/kasp.proto	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ enforcer-ng/src/policy/kasp.proto	(.../OpenDNSSEC-CDS)	(working copy)
@@ -98,6 +98,7 @@
 	repeated Ksk ksk = 6 [(xml).path="KSK"]; // Key Signing Keys (KSK) parameters.
 	repeated Zsk zsk = 7 [(xml).path="ZSK"]; // Zone Signing Keys (ZSK) parameters.
 	repeated Csk csk = 8 [(xml).path="CSK"]; // Combined Signing Keys (CSK) paramters.
+	optional Cds cds = 9 [(xml).path="CDS"]; // Use CDS for key rollover ?
 }
 
 // These values correspond directly to the minimize flags. 3,6 & 7
@@ -120,7 +121,6 @@
 	CskDoubleSignature = 4;	//minimize DS
 	CskPrePublication = 5;	//minimize DS and RRSIG
 }
-
 message Ksk {
 	// anykey = generic key definition
 	required uint32 algorithm = 1 [(xml).path="Algorithm"]; // ksk algorithm (maxInclusive 255)
@@ -161,6 +161,10 @@
 	optional CskRollType rollover_type = 8 [default = CskPrePublication, (xml).path="CskRollType"]; // user friendly way of configure minimize flags
 }
 
+message Cds {
+	repeated Digest digest = 1 [(xml).path="Digest"]; // Digest algorithm [0..255]
+}
+
 message Zone {
 	required int32 propagationdelay = 1 [(xml).path="PropagationDelay", (xml).type=duration]; // Expected propagation delay in child publication
 	optional int32 ttl = 2 [(xml).path="SOA/TTL", (xml).type=duration]; // ttl of the soa
@@ -187,3 +191,6 @@
 	optional bool partial = 1 [(xml).path="Partial"]; // Do a partial audit ?
 }
 
+message Digest {
+	required uint32 digest_type = 1 [(xml).path="Type"]; // Digest algorithm [0..255]
+}
Index: enforcer-ng/src/keystate/keystate.proto
===================================================================
--- enforcer-ng/src/keystate/keystate.proto	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ enforcer-ng/src/keystate/keystate.proto	(.../OpenDNSSEC-CDS)	(working copy)
@@ -88,6 +88,7 @@
     optional bool active_ksk = 16 [default = false, (xml).path="ActiveKSK"];
     optional dsatparent ds_at_parent = 17 [default = unsubmitted, (xml).path="DSAtParent"];
     optional uint32 keytag = 18 [default = 0, (xml).path="Keytag"];
+    optional bool active_cds = 19 [default = false, (xml).path="ActiveCDS"];
 }
 
 message KeyState {
Index: enforcer-ng/src/enforcer/enforcer.cpp
===================================================================
--- enforcer-ng/src/enforcer/enforcer.cpp	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ enforcer-ng/src/enforcer/enforcer.cpp	(.../OpenDNSSEC-CDS)	(working copy)
@@ -1434,6 +1434,7 @@
 		k.setPublish(getState(k, DK, NULL) == OMN || getState(k, DK, NULL) == RUM);
 		k.setActiveKSK(getState(k, RD, NULL) == OMN || getState(k, RD, NULL) == RUM);
 		k.setActiveZSK(getState(k, RS, NULL) == OMN || getState(k, RS, NULL) == RUM);
+		k.setActiveCDS(getState(k, DS, NULL) == OMN || getState(k, DS, NULL) == RUM);
 	}
 
 	minTime(policy_return_time, zone_return_time);
Index: enforcer-ng/src/enforcer/enforcerdata.h
===================================================================
--- enforcer-ng/src/enforcer/enforcerdata.h	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ enforcer-ng/src/enforcer/enforcerdata.h	(.../OpenDNSSEC-CDS)	(working copy)
@@ -230,6 +230,7 @@
     virtual void setPublish(bool value) = 0;
     virtual void setActiveZSK(bool value) = 0;
     virtual void setActiveKSK(bool value) = 0;
+    virtual void setActiveCDS(bool value) = 0;
     
     virtual void setDsAtParent(DsAtParent value) = 0;
     virtual DsAtParent dsAtParent() = 0;
Index: enforcer-ng/src/enforcer/enforcerzone.cpp
===================================================================
--- enforcer-ng/src/enforcer/enforcerzone.cpp	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ enforcer-ng/src/enforcer/enforcerzone.cpp	(.../OpenDNSSEC-CDS)	(working copy)
@@ -208,6 +208,11 @@
     _keydata->set_active_ksk(value);
 }
 
+void KeyDataPB::setActiveCDS(bool value)
+{
+    _keydata->set_active_cds(value);
+}
+
 DsAtParent KeyDataPB::dsAtParent()
 {
     return (DsAtParent)_keydata->ds_at_parent();
Index: enforcer-ng/src/enforcer/enforcerzone.h
===================================================================
--- enforcer-ng/src/enforcer/enforcerzone.h	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ enforcer-ng/src/enforcer/enforcerzone.h	(.../OpenDNSSEC-CDS)	(working copy)
@@ -102,6 +102,7 @@
     virtual void setPublish(bool value);
     virtual void setActiveZSK(bool value);
     virtual void setActiveKSK(bool value);
+    virtual void setActiveCDS(bool value);
     
     /* Current state of the DS record at the parent */
     virtual DsAtParent dsAtParent();
Index: enforcer-ng/src/signconf/signconf_task.cpp
===================================================================
--- enforcer-ng/src/signconf/signconf_task.cpp	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ enforcer-ng/src/signconf/signconf_task.cpp	(.../OpenDNSSEC-CDS)	(working copy)
@@ -142,7 +142,7 @@
 	
 	for (int k=0; k<ks_zone->keys_size(); ++k) {
 		const ::ods::keystate::KeyData &ks_key = ks_zone->keys(k);
-		
+
 		// first check whether we actually should write this key into the
 		// signer configuration.
 		if (!ks_key.publish() && !ks_key.active_ksk() && !ks_key.active_zsk())
@@ -169,7 +169,7 @@
 						(ks_key.role() == ::ods::keystate::ZSK
 						 || ks_key.role() == ::ods::keystate::CSK) );
 		sc_key->set_publish( ks_key.publish() );
-		
+
 		// The deactivate flag was intended to allow smooth key rollover.
 		// With the deactivate flag present a normal rollover would be 
 		// performed where signatures would be replaced immmediately.
@@ -179,6 +179,27 @@
 		// new signatures.
 		// Currently this flag is not supported by the signer engine.
 		// sc_key->set_deactivate(  );
+
+		if (policy->keys().has_cds()) {
+			const ::ods::kasp::Cds &kp_cds = policy->keys().cds();
+			ods_log_info("Kasp has cds info...");
+			if (ks_key.active_cds()) {
+				ods_log_info("Key cds is active..., #digests=%u", kp_cds.digest_size());
+				::ods::signconf::CDS *sc_cds = sc_key->mutable_cds();
+				for (int l=0; l < kp_cds.digest_size(); ++l) {
+					::ods::signconf::Digest *sc_digest = sc_cds->add_digest();
+					ods_log_info("Key cds add digest...");
+					sc_digest->set_digest_type(kp_cds.digest(l).digest_type());
+				}
+			} else {
+				ods_log_info("Key cds is not active...");
+				sc_key->clear_cds();
+			}
+		} else {
+			ods_log_info("Kasp has no cds info...");
+			sc_key->clear_cds();
+		}
+
 	}
 	
 	const ::ods::kasp::Zone &kp_zone = policy->zone();
Index: enforcer-ng/src/signconf/signconf.proto
===================================================================
--- enforcer-ng/src/signconf/signconf.proto	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ enforcer-ng/src/signconf/signconf.proto	(.../OpenDNSSEC-CDS)	(working copy)
@@ -95,8 +95,17 @@
 	optional bool zsk = 5  [(xml).path="ZSK"]; // sign all non-DNSKEY RRsets with this key ?
     optional bool publish = 6 [(xml).path="Publish"]; // include this key in the zonefile ?
     optional bool deactivate = 7 [(xml).path="Deactivate"]; // deactivate this key (i.e. do not recycle any signatures)
+    optional CDS cds = 8 [(xml).path="CDS"]; // create CDS RR for this key ?
 }
 
+message CDS {
+    repeated Digest digest = 1 [(xml).path="Digest"]; // Digest algorithm [0..255]
+}
+
+message Digest {
+    required uint32 digest_type = 1 [(xml).path="Type"]; // Digest algorithm [0..255]
+}
+
 enum serial {
     counter = 1; // use an increasing counter (but use the serial from the unsigned zone if possible)
     datecounter = 2; // use increasing counter in YYYYMMDDxx format (xx is incremented within each day)
Index: enforcer-ng/src/xmlext-pb/test/main.cpp
===================================================================
--- enforcer-ng/src/xmlext-pb/test/main.cpp	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ enforcer-ng/src/xmlext-pb/test/main.cpp	(.../OpenDNSSEC-CDS)	(working copy)
@@ -37,6 +37,7 @@
 				"<Algorithm length=\"2048\">8</Algorithm>\n"
 				"<Lifetime>P1Y</Lifetime>\n"
 				"<Repository>SoftHSM</Repository>\n"
+				"<CDS><DigestType>1</DigestType></CDS>\n"
 			"</KSK>\n"
 			"<ZSK>\n"
 				"<Algorithm length=\"1024\">8</Algorithm>\n"
Index: m4/opendnssec_common.m4
===================================================================
--- m4/opendnssec_common.m4	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ m4/opendnssec_common.m4	(.../OpenDNSSEC-CDS)	(working copy)
@@ -83,6 +83,7 @@
 AC_DEFINE_UNQUOTED(ODS_SE_MAX_BACKOFF,   [3600],                             [Number of seconds the OpenDNSSEC signer engine should backoff when a task failed])
 AC_DEFINE_UNQUOTED(ODS_SE_WORKERTHREADS, [4],                                [Default number of worker threads for the OpenDNSSEC signer engine])
 AC_DEFINE_UNQUOTED(ODS_SE_STOP_RESPONSE, ["Engine shut down."],              [Shutdown message for the OpenDNSSEC signer client])
+AC_DEFINE_UNQUOTED(ODS_SE_FILE_MAGIC_V4, [";OpenDNSSEC2.0-backup-v4"],       [File magic for storing backups from the OpenDNSSEC signer engine])
 AC_DEFINE_UNQUOTED(ODS_SE_FILE_MAGIC_V3, [";OpenDNSSEC-backup-v3"],          [File magic for storing backups from the OpenDNSSEC signer engine])
 AC_DEFINE_UNQUOTED(ODS_SE_FILE_MAGIC_V2, [";ODSSE2"],                        [File magic for storing backups from the OpenDNSSEC signer engine])
 AC_DEFINE_UNQUOTED(ODS_SE_FILE_MAGIC_V1, [";ODSSE1"],                        [File magic for storing backups from the OpenDNSSEC signer engine])
Index: conf/kasp.xml.in
===================================================================
--- conf/kasp.xml.in	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ conf/kasp.xml.in	(.../OpenDNSSEC-CDS)	(working copy)
@@ -48,6 +48,10 @@
 			<PublishSafety>PT3600S</PublishSafety>
 			<!-- <ShareKeys/> -->
 			<Purge>P14D</Purge>
+			<CDS>
+				<Digest><Type>SHA1</Type></Digest>
+				<Digest><Type>SHA256</Type></Digest>
+			</CDS>
 
 			<!-- Parameters for KSK only -->
 			<KSK>
Index: conf/signconf.rnc
===================================================================
--- conf/signconf.rnc	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ conf/signconf.rnc	(.../OpenDNSSEC-CDS)	(working copy)
@@ -76,7 +76,11 @@
 				element Publish { empty }?,
 				
 				# deactivate this key (i.e. do not recycle any signatures)
-				element Deactivate { empty }?
+				element Deactivate { empty }?,
+
+				# create CDS rr for this DNSKEY?
+				cds?
+
 			}*
 		},
 
@@ -116,4 +120,11 @@
 		}
 	}
 
+# CDS record
+cds = element CDS {
+	element	Digest {
+		element Type { xsd:nonNegativeInteger { maxInclusive = "255" } }
+	}+
+}
+
 maxzonettl = element MaxZoneTTL { xsd:duration }
Index: conf/kasp.rnc
===================================================================
--- conf/kasp.rnc	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ conf/kasp.rnc	(.../OpenDNSSEC-CDS)	(working copy)
@@ -85,6 +85,9 @@
 			# enforcer may purge keys after this amount of time
 			element Purge { xsd:duration }?,
 
+			# use CDS for key rollover?
+			cds?,
+
 			# Key Signing Keys (KSK) parameters
 			element KSK {
 				# generic key definition, see below
@@ -214,3 +217,12 @@
 propagationdelay = element PropagationDelay { xsd:duration }
 
 partial = element Partial { empty }
+
+
+# CDS record
+cds = element CDS {
+	element Digest {
+		element Type { xsd:nonNegativeInteger { maxInclusive = "255" } }
+	}+
+}
+
Index: conf/signconf.xml.in
===================================================================
--- conf/signconf.xml.in	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ conf/signconf.xml.in	(.../OpenDNSSEC-CDS)	(working copy)
@@ -36,6 +36,9 @@
 				<Locator>DFE7265B783F418685380AA784C2F31D</Locator>
 				<KSK/>
 				<Publish/>
+				<CDS>
+					<DigestType>1</DigestType>
+				</CDS>
 			</Key>
 
 			<Key>
Index: signer/src/parser/signconfparser.c
===================================================================
--- signer/src/parser/signconfparser.c	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ signer/src/parser/signconfparser.c	(.../OpenDNSSEC-CDS)	(working copy)
@@ -45,7 +45,13 @@
 
 static const char* parser_str = "parser";
 
+static void
+ods_setbit(int* m, int b)
+{
+    *m |= 1 << (b-1);
+}
 
+
 /**
  * Parse keys from the signer configuration file.
  *
@@ -57,6 +63,8 @@
     xmlXPathContextPtr xpathCtx = NULL;
     xmlXPathObjectPtr xpathObj = NULL;
     xmlNode* curNode = NULL;
+    xmlNode* childNode = NULL;
+    xmlNode* childNode2 = NULL;
     xmlChar* xexpr = NULL;
     key_type* new_key = NULL;
     keylist_type* kl = NULL;
@@ -63,6 +71,7 @@
     char* locator = NULL;
     char* flags = NULL;
     char* algorithm = NULL;
+    int cds_digest_types;
     int ksk, zsk, publish, i;
 
     if (!cfgfile || !sc) {
@@ -101,6 +110,7 @@
             locator = NULL;
             flags = NULL;
             algorithm = NULL;
+            cds_digest_types = 0;
             ksk = 0;
             zsk = 0;
             publish = 0;
@@ -117,6 +127,20 @@
                     ksk = 1;
                 } else if (xmlStrEqual(curNode->name, (const xmlChar *)"ZSK")) {
                     zsk = 1;
+                } else if (xmlStrEqual(curNode->name, (const xmlChar *)"CDS")) {
+                    childNode = curNode->children;
+                    while (childNode) {
+                        if (xmlStrEqual(childNode->name, (const xmlChar *)"Digest")) {
+                            childNode2 = childNode->children;
+                            while (childNode2) {
+                                if (xmlStrEqual(childNode2->name, (const xmlChar *)"Type")) {
+                                    ods_setbit(&cds_digest_types, atoi((char *) xmlNodeGetContent(childNode2)));
+                                }
+                                childNode2 = childNode2->next;
+                            }
+                        }
+                        childNode = childNode->next;
+                    }
                 } else if (xmlStrEqual(curNode->name, (const xmlChar *)"Publish")) {
                     publish = 1;
                 }
@@ -130,7 +154,8 @@
                     new_key->flags == (uint32_t) atoi(flags) &&
                     new_key->publish == publish &&
                     new_key->ksk == ksk &&
-                    new_key->zsk == zsk) {
+                    new_key->zsk == zsk &&
+                    new_key->cds_digest_types == cds_digest_types) {
                     /* duplicate */
                     ods_log_warning("[%s] unable to push duplicate key %s "
                         "to keylist, skipping", parser_str, locator);
@@ -137,7 +162,7 @@
                 } else {
                     new_key = keylist_push(kl, locator,
                         (uint8_t) atoi(algorithm), (uint32_t) atoi(flags),
-                        publish, ksk, zsk);
+                        publish, ksk, zsk, cds_digest_types);
                 }
             } else {
                 ods_log_error("[%s] unable to push key to keylist: <Key> "
Index: signer/src/signer/rrset.c
===================================================================
--- signer/src/signer/rrset.c	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ signer/src/signer/rrset.c	(.../OpenDNSSEC-CDS)	(working copy)
@@ -564,6 +564,10 @@
     if (rrset->rrtype == LDNS_RR_TYPE_DNSKEY) {
         return 0;
     }
+    /* CDS RRset always needs to be signed with active key */
+    if (rrset->rrtype == LDNS_RR_TYPE_CDS) {
+        return 0;
+    }
     /* Let's look for RRSIGs from inactive ZSKs */
     for (i=0; i < rrset->rrsig_count; i++) {
         /* Same algorithm? */
@@ -723,14 +727,16 @@
          &inception, &expiration);
     /* Walk keys */
     for (i=0; i < zone->signconf->keys->count; i++) {
-        /* ZSKs don't sign DNSKEY RRset */
+        /* ZSKs don't sign DNSKEY|CDS RRset */
         if (!zone->signconf->keys->keys[i].zsk &&
-            rrset->rrtype != LDNS_RR_TYPE_DNSKEY) {
+            rrset->rrtype != LDNS_RR_TYPE_DNSKEY &&
+            rrset->rrtype != LDNS_RR_TYPE_CDS) {
             continue;
         }
-        /* KSKs only sign DNSKEY RRset */
+        /* KSKs only sign DNSKEY|CDS RRset */
         if (!zone->signconf->keys->keys[i].ksk &&
-            rrset->rrtype == LDNS_RR_TYPE_DNSKEY) {
+            (rrset->rrtype == LDNS_RR_TYPE_DNSKEY ||
+            rrset->rrtype == LDNS_RR_TYPE_CDS)) {
             continue;
         }
         /* Additional rules for signatures */
Index: signer/src/signer/domain.c
===================================================================
--- signer/src/signer/domain.c	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ signer/src/signer/domain.c	(.../OpenDNSSEC-CDS)	(working copy)
@@ -290,8 +290,9 @@
     rrset = domain->rrsets;
     while (rrset) {
         if (rrset->rrtype == LDNS_RR_TYPE_NSEC3PARAMS ||
-            rrset->rrtype == LDNS_RR_TYPE_DNSKEY) {
-            /* always do full diff on NSEC3PARAMS | DNSKEY RRset */
+            rrset->rrtype == LDNS_RR_TYPE_DNSKEY ||
+            rrset->rrtype == LDNS_RR_TYPE_CDS) {
+            /* always do full diff on NSEC3PARAMS | DNSKEY | CDS RRset */
             rrset_diff(rrset, 0);
         } else {
             rrset_diff(rrset, is_ixfr);
Index: signer/src/signer/keys.c
===================================================================
--- signer/src/signer/keys.c	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ signer/src/signer/keys.c	(.../OpenDNSSEC-CDS)	(working copy)
@@ -118,7 +118,8 @@
  */
 key_type*
 keylist_push(keylist_type* kl, const char* locator,
-    uint8_t algorithm, uint32_t flags, int publish, int ksk, int zsk)
+    uint8_t algorithm, uint32_t flags, int publish, int ksk, int zsk,
+    int digest_types)
 {
     key_type* keys_old = NULL;
     signconf_type* sc = NULL;
@@ -146,6 +147,9 @@
     kl->keys[kl->count -1].publish = publish;
     kl->keys[kl->count -1].ksk = ksk;
     kl->keys[kl->count -1].zsk = zsk;
+    kl->keys[kl->count -1].cds_digest_types = digest_types;
+    kl->keys[kl->count -1].cds[0] = NULL;
+    kl->keys[kl->count -1].cds[1] = NULL;
     kl->keys[kl->count -1].dnskey = NULL;
     kl->keys[kl->count -1].hsmkey = NULL;
     kl->keys[kl->count -1].params = NULL;
@@ -175,9 +179,16 @@
     if (key->zsk) {
         fprintf(fd, "\t\t\t\t<ZSK />\n");
     }
+    if (key->zsk) {
+        fprintf(fd, "\t\t\t\t<ZSK />\n");
+    }
     if (key->publish) {
         fprintf(fd, "\t\t\t\t<Publish />\n");
     }
+    if (key->cds_digest_types) {
+        fprintf(fd, "\t\t\t\t<CDS><DigestType>%d</DigestType></CDS>\n",
+            key->cds_digest_types);
+    }
     fprintf(fd, "\t\t\t</Key>\n");
     fprintf(fd, "\n");
     return;
@@ -195,8 +206,9 @@
         return;
     }
     ods_log_debug("[%s] zone %s key: LOCATOR[%s] FLAGS[%u] ALGORITHM[%u] "
-        "KSK[%i] ZSK[%i] PUBLISH[%i]", key_str, name?name:"(null)", key->locator,
-        key->flags, key->algorithm, key->ksk, key->zsk, key->publish);
+        "KSK[%i] ZSK[%i] CDS[%i] PUBLISH[%i]", key_str, name?name:"(null)",
+        key->locator, key->flags, key->algorithm, key->ksk, key->zsk,
+        key->cds_digest_types, key->publish);
     return;
 }
 
@@ -248,6 +260,8 @@
         return;
     }
     /* ldns_rr_free(key->dnskey); */
+    /* ldns_rr_free(key->cds[0]); */
+    /* ldns_rr_free(key->cds[1]); */
     hsm_key_free(key->hsmkey);
     hsm_sign_params_free(key->params);
     free((void*) key->locator);
@@ -287,9 +301,10 @@
         return;
     }
     fprintf(fd, ";;Key: locator %s algorithm %u flags %u publish %i ksk %i "
-        "zsk %i\n", key->locator, (unsigned) key->algorithm,
-        (unsigned) key->flags, key->publish, key->ksk, key->zsk);
-    if (strcmp(version, ODS_SE_FILE_MAGIC_V2) == 0) {
+        "zsk %i cds %i\n", key->locator, (unsigned) key->algorithm,
+        (unsigned) key->flags, key->publish, key->ksk, key->zsk,
+        key->cds_digest_types);
+    if (strcmp(version, ODS_SE_FILE_MAGIC_V3) == 0) {
         if (key->dnskey) {
             (void)util_rr_print(fd, key->dnskey);
         }
@@ -312,6 +327,7 @@
     int publish = 0;
     int ksk = 0;
     int zsk = 0;
+    int cds = 0;
 
     ods_log_assert(fd);
 
@@ -333,8 +349,12 @@
         }
         return NULL;
     }
+    if (!backup_read_check_str(fd, "cds") ||
+        !backup_read_int(fd, &cds)) {
+       ods_log_warning("[%s] CDS not in backup, default to 0", key_str);
+    }
     /* key ok */
-    return keylist_push(kl, locator, algorithm, flags, publish, ksk, zsk);
+    return keylist_push(kl, locator, algorithm, flags, publish, ksk, zsk, cds);
 }
 
 
Index: signer/src/signer/zone.c
===================================================================
--- signer/src/signer/zone.c	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ signer/src/signer/zone.c	(.../OpenDNSSEC-CDS)	(working copy)
@@ -221,6 +221,56 @@
 
 
 /**
+ * Publish CDS.
+ *
+ */
+static ods_status
+zone_publish_cds(zone_type* zone, int digest_type, int i, int pos, int ttl)
+{
+    ods_status status = ODS_STATUS_OK;
+    rrset_type* rrset = NULL;
+    rr_type* cds = NULL;
+    ods_log_assert(zone);
+    ods_log_assert(digest_type > 0);
+    ods_log_assert(digest_type <= 2);
+    ods_log_assert(pos == 0 || pos == 1);
+    if (!zone->signconf->keys->keys[i].cds[pos]) {
+        /* get cds */
+        zone->signconf->keys->keys[i].cds[pos] = ldns_key_rr2ds(
+            zone->signconf->keys->keys[i].dnskey, digest_type);
+        if (!zone->signconf->keys->keys[i].cds[pos]) {
+            ods_log_error("[%s] unable to publish dnskeys for zone %s: "
+                "error creating dnskey", zone_str, zone->name);
+            return ODS_STATUS_ERR;
+        }
+    }
+    ods_log_debug("[%s] publish %s CDS locator %s", zone_str,
+        zone->name, zone->signconf->keys->keys[i].locator);
+    ods_log_assert(zone->signconf->keys->keys[i].cds[pos]);
+    ldns_rr_set_type(zone->signconf->keys->keys[i].cds[pos], LDNS_RR_TYPE_CDS);
+    ldns_rr_set_ttl(zone->signconf->keys->keys[i].cds[pos], ttl);
+    ldns_rr_set_class(zone->signconf->keys->keys[i].cds[pos], zone->klass);
+    status = zone_add_rr(zone, zone->signconf->keys->keys[i].cds[pos], 0);
+    if (status == ODS_STATUS_UNCHANGED) {
+        /* rr already exists, adjust pointer */
+        rrset = zone_lookup_rrset(zone, zone->apex, LDNS_RR_TYPE_CDS);
+        ods_log_assert(rrset);
+        cds = rrset_lookup_rr(rrset, zone->signconf->keys->keys[i].cds[pos]);
+        ods_log_assert(cds);
+        if (cds->rr != zone->signconf->keys->keys[i].cds[pos]) {
+            ldns_rr_free(zone->signconf->keys->keys[i].cds[pos]);
+        }
+        zone->signconf->keys->keys[i].cds[pos] = cds->rr;
+        status = ODS_STATUS_OK;
+    } else if (status != ODS_STATUS_OK) {
+        ods_log_error("[%s] unable to publish dnskeys for zone %s: "
+            "error adding cds", zone_str, zone->name);
+    }
+    return status;
+}
+
+
+/**
  * Publish the keys as indicated by the signer configuration.
  *
  */
@@ -299,6 +349,13 @@
                 "error adding dnskey", zone_str, zone->name);
             break;
         }
+        /* publish cds */
+        if (zone->signconf->keys->keys[i].cds_digest_types & CDS_DIGEST_SHA1) {
+            zone_publish_cds(zone, CDS_DIGEST_SHA1, i, 0, ttl);
+	}
+        if (zone->signconf->keys->keys[i].cds_digest_types & CDS_DIGEST_SHA2) {
+            zone_publish_cds(zone, CDS_DIGEST_SHA2, i, 1, ttl);
+	}
     }
     /* done */
     hsm_destroy_context(ctx);
@@ -316,6 +373,7 @@
     uint16_t i = 0;
     rrset_type* rrset = NULL;
     rr_type* dnskey = NULL;
+    rr_type* cds = NULL;
     if (!zone || !zone->signconf || !zone->signconf->keys) {
         return;
     }
@@ -331,6 +389,25 @@
             }
         }
     }
+    /* unlink cds rrs */
+    for (i=0; i < zone->signconf->keys->count; i++) {
+        if (rrset && zone->signconf->keys->keys[i].cds[0]) {
+            cds = rrset_lookup_rr(rrset,
+                zone->signconf->keys->keys[i].cds[0]);
+            if (cds && !cds->exists &&
+                cds->rr == zone->signconf->keys->keys[i].cds[0]) {
+                zone->signconf->keys->keys[i].cds[0] = NULL;
+            }
+        }
+        if (rrset && zone->signconf->keys->keys[i].cds[1]) {
+            cds = rrset_lookup_rr(rrset,
+                zone->signconf->keys->keys[i].cds[1]);
+            if (cds && !cds->exists &&
+                cds->rr == zone->signconf->keys->keys[i].cds[1]) {
+                zone->signconf->keys->keys[i].cds[1] = NULL;
+            }
+        }
+    }
     /* done */
     return;
 }
Index: signer/src/signer/keys.h
===================================================================
--- signer/src/signer/keys.h	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ signer/src/signer/keys.h	(.../OpenDNSSEC-CDS)	(working copy)
@@ -48,6 +48,10 @@
 #include <libhsmdns.h>
 
 
+#define CDS_DIGEST_SHA1 0x01
+#define CDS_DIGEST_SHA2 0x02
+
+
 /**
  * Key.
  *
@@ -55,6 +59,7 @@
 typedef struct key_struct key_type;
 struct key_struct {
     ldns_rr* dnskey;
+    ldns_rr* cds[2];
     hsm_key_t* hsmkey;
     hsm_sign_params_t* params;
     const char* locator;
@@ -63,6 +68,7 @@
     int publish;
     int ksk;
     int zsk;
+    int cds_digest_types;
 };
 
 /**
@@ -111,11 +117,13 @@
  * \param[in] publish if true, publish key as a DNSKEY
  * \param[in] ksk if true, sign DNSKEY RRset with this key
  * \param[in] zsk if true, sign all but DNSKEY RRset with this key
+ * \param[in] digest_types digest types for CDS RR
  * \return key_type* key
  *
  */
 key_type* keylist_push(keylist_type* kl, const char* locator,
-    uint8_t algorithm, uint32_t flags, int publish, int ksk, int zsk);
+    uint8_t algorithm, uint32_t flags, int publish, int ksk, int zsk,
+    int digest_types);
 
 /**
  * Print key list.
Index: signer/src/shared/util.c
===================================================================
--- signer/src/shared/util.c	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ signer/src/shared/util.c	(.../OpenDNSSEC-CDS)	(working copy)
@@ -55,6 +55,7 @@
     }
     type = ldns_rr_get_type(rr);
     return (type == LDNS_RR_TYPE_RRSIG ||
+            type == LDNS_RR_TYPE_CDS ||
             type == LDNS_RR_TYPE_NSEC ||
             type == LDNS_RR_TYPE_NSEC3 ||
             type == LDNS_RR_TYPE_NSEC3PARAMS);
Index: signer/src/shared/hsm.c
===================================================================
--- signer/src/shared/hsm.c	(.../OpenDNSSEC-enforcer-ng)	(revision 7133)
+++ signer/src/shared/hsm.c	(.../OpenDNSSEC-CDS)	(working copy)
@@ -94,6 +94,14 @@
         /* DNSKEY still exists in zone */
         key->dnskey = NULL;
     }
+    if (key->cds[0]) {
+        /* CDS still exists in zone */
+        key->cds[0] = NULL;
+    }
+    if (key->cds[1]) {
+        /* CDS still exists in zone */
+        key->cds[1] = NULL;
+    }
     if (key->hsmkey) {
         hsm_key_free(key->hsmkey);
         key->hsmkey = NULL;
