From 5700cec6729fd637cc23180b00ecdfb81f29d253 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Vavru=C5=A1a?= <marek.vavrusa@nic.cz>
Date: Wed, 23 Sep 2015 09:37:24 +0200
Subject: [PATCH] lib/dnssec: allow KSK DNSKEY records for TA

this allows the classic workflow, kdig for root DNSKEY
records to a key file and let it start
---
 lib/dnssec/ta.c | 79 +++++++++++++++++++++++++++++++++++++------------
 1 file changed, 60 insertions(+), 19 deletions(-)

diff --git a/lib/dnssec/ta.c b/lib/dnssec/ta.c
index 2ecb12292..7e3daa2b2 100644
--- a/lib/dnssec/ta.c
+++ b/lib/dnssec/ta.c
@@ -18,6 +18,8 @@
 #include <libknot/rdataset.h>
 #include <libknot/rrset.h>
 #include <libknot/packet/wire.h>
+#include <dnssec/key.h>
+#include <dnssec/error.h>
 
 #include "lib/defines.h"
 #include "lib/dnssec/ta.h"
@@ -27,28 +29,44 @@ knot_rrset_t *kr_ta_get(map_t *trust_anchors, const knot_dname_t *name)
 	return map_get(trust_anchors, (const char *)name);
 }
 
-int kr_ta_add(map_t *trust_anchors, const knot_dname_t *name, uint16_t type,
-               uint32_t ttl, const uint8_t *rdata, uint16_t rdlen)
+/* @internal Create DS from DNSKEY, caller MUST free dst if successful. */
+static int dnskey2ds(dnssec_binary_t *dst, const knot_dname_t *owner, const uint8_t *rdata, uint16_t rdlen)
 {
-	if (!trust_anchors || !name) {
-		return kr_error(EINVAL);
+	dnssec_key_t *key = NULL;
+	int ret = dnssec_key_new(&key);
+	if (ret != DNSSEC_EOK) {
+		return kr_error(ENOMEM);
 	}
-
-	/* Convert DNSKEY records to DS */
-	switch (type) {
-	case KNOT_RRTYPE_DS: break; /* OK */
-	case KNOT_RRTYPE_DNSKEY:
-#warning TODO: convert DNSKEY -> DS here
-		return kr_error(ENOSYS);
-		break;
-	default: return kr_error(EINVAL);
+	/* Create DS from DNSKEY and reinsert */
+	const dnssec_binary_t key_data = { .size = rdlen, .data = (uint8_t *)rdata };
+	ret = dnssec_key_set_rdata(key, &key_data);
+	if (ret == DNSSEC_EOK) {
+		/* Accept only KSK (257) to TA store */
+		if (dnssec_key_get_flags(key) == 257)  {
+			ret = dnssec_key_set_dname(key, owner);
+		} else {
+			ret = DNSSEC_EINVAL;
+		}
+		if (ret == DNSSEC_EOK) {
+			ret = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA256, dst);
+		}
+	}
+	dnssec_key_free(key);
+	/* Pick some sane error code */
+	if (ret != DNSSEC_EOK) {
+		return kr_error(ENOMEM);
 	}
+	return kr_ok();
+}
 
-	/* Create new RRSet or use existing */
+/* @internal Insert new TA to trust anchor set, rdata MUST be of DS type. */
+static int insert_ta(map_t *trust_anchors, const knot_dname_t *name,
+                     uint32_t ttl, const uint8_t *rdata, uint16_t rdlen)
+{
 	bool is_new_key = false;
 	knot_rrset_t *ta_rr = kr_ta_get(trust_anchors, name);
-	if (!ta_rr) { 
-		ta_rr = knot_rrset_new(name, type, KNOT_CLASS_IN, NULL);
+	if (!ta_rr) {
+		ta_rr = knot_rrset_new(name, KNOT_RRTYPE_DS, KNOT_CLASS_IN, NULL);
 		is_new_key = true;
 	}
 	/* Merge-in new key data */
@@ -56,14 +74,37 @@ int kr_ta_add(map_t *trust_anchors, const knot_dname_t *name, uint16_t type,
 		knot_rrset_free(&ta_rr, NULL);
 		return kr_error(ENOMEM);
 	}
-	/* Reinsert */
 	if (is_new_key) {
-		map_set(trust_anchors, (const char *)name, ta_rr);
+		return map_set(trust_anchors, (const char *)name, ta_rr);
 	}
-
 	return kr_ok();	
 }
 
+int kr_ta_add(map_t *trust_anchors, const knot_dname_t *name, uint16_t type,
+              uint32_t ttl, const uint8_t *rdata, uint16_t rdlen)
+{
+	if (!trust_anchors || !name) {
+		return kr_error(EINVAL);
+	}
+
+	/* DS/DNSEY types are accepted, for DNSKEY we
+	 * need to compute a DS digest. */
+	if (type == KNOT_RRTYPE_DS) {
+		return insert_ta(trust_anchors, name, ttl, rdata, rdlen);
+	} else if (type == KNOT_RRTYPE_DNSKEY) {
+		dnssec_binary_t ds_rdata = { 0, };
+		int ret = dnskey2ds(&ds_rdata, name, rdata, rdlen);
+		if (ret != 0) {
+			return ret;
+		}
+		ret = insert_ta(trust_anchors, name, ttl, ds_rdata.data, ds_rdata.size);
+		dnssec_binary_free(&ds_rdata);
+		return ret;
+	} else { /* Invalid type for TA */
+		return kr_error(EINVAL);
+	}
+}
+
 int kr_ta_covers(map_t *trust_anchors, const knot_dname_t *name)
 {
 	while(name) {
-- 
GitLab